@pagopa/dx-mcpserver 0.1.4 → 0.1.5
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/__tests__/http-endpoints.test.js +0 -6
- package/dist/__tests__/index.test.js +48 -4
- package/dist/services/__tests__/bedrock.test.js +2 -5
- package/dist/services/bedrock.js +1 -1
- package/dist/tools/__tests__/QueryValidation.test.js +2 -6
- package/dist/tools/__tests__/registry.test.js +0 -17
- package/package.json +4 -4
- package/dist/__tests__/__mocks__/handlers.js +0 -48
|
@@ -167,12 +167,6 @@ describe("HTTP Endpoints Integration Tests", () => {
|
|
|
167
167
|
expect(data).toHaveProperty("query");
|
|
168
168
|
expect(data).toHaveProperty("results");
|
|
169
169
|
expect(Array.isArray(data.results)).toBe(true);
|
|
170
|
-
if (data.results.length > 0) {
|
|
171
|
-
const result = data.results[0];
|
|
172
|
-
expect(result).toHaveProperty("content");
|
|
173
|
-
expect(result).toHaveProperty("score");
|
|
174
|
-
expect(typeof result.score).toBe("number");
|
|
175
|
-
}
|
|
176
170
|
});
|
|
177
171
|
it("should use default number_of_results when not provided", async () => {
|
|
178
172
|
const response = await fetch(`${baseUrl}/search`, {
|
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const mockTool = {
|
|
4
|
+
annotations: {
|
|
5
|
+
destructiveHint: false,
|
|
6
|
+
idempotentHint: true,
|
|
7
|
+
openWorldHint: true,
|
|
8
|
+
readOnlyHint: true,
|
|
9
|
+
title: "Test Tool",
|
|
10
|
+
},
|
|
11
|
+
description: "A test tool",
|
|
12
|
+
execute: vi.fn(async (args) => {
|
|
13
|
+
const parsedResult = z.object({ input: z.string() }).safeParse(args);
|
|
14
|
+
if (!parsedResult.success) {
|
|
15
|
+
return "Error: Invalid input";
|
|
16
|
+
}
|
|
17
|
+
return `Tool executed with: ${parsedResult.data.input}`;
|
|
18
|
+
}),
|
|
19
|
+
name: "TestTool",
|
|
20
|
+
parameters: z.object({
|
|
21
|
+
input: z.string().min(1, "Input cannot be empty"),
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
export const mockCatalogEntry = {
|
|
25
|
+
category: "test",
|
|
26
|
+
enabled: true,
|
|
27
|
+
id: "test-prompt",
|
|
28
|
+
metadata: {
|
|
29
|
+
description: "A test prompt for unit testing",
|
|
30
|
+
title: "Test Prompt",
|
|
31
|
+
},
|
|
32
|
+
prompt: {
|
|
33
|
+
arguments: [
|
|
34
|
+
{ description: "First argument", name: "arg1", required: true },
|
|
35
|
+
{ description: "Second argument", name: "arg2", required: false },
|
|
36
|
+
],
|
|
37
|
+
description: "A test prompt",
|
|
38
|
+
load: async (args) => `Prompt loaded with args: ${JSON.stringify(args)}`,
|
|
39
|
+
name: "TestPrompt",
|
|
40
|
+
},
|
|
41
|
+
tags: ["test"],
|
|
42
|
+
};
|
|
43
|
+
export const mockPromptEntry = {
|
|
44
|
+
catalogEntry: mockCatalogEntry,
|
|
45
|
+
prompt: {
|
|
46
|
+
load: vi.fn(async (args) => `Prompt loaded with args: ${JSON.stringify(args)}`),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
3
49
|
describe("MCP Server Handlers", () => {
|
|
4
50
|
describe("Tool Handler Validation", () => {
|
|
5
51
|
it("should validate tool arguments against the Zod schema", async () => {
|
|
@@ -11,9 +57,7 @@ describe("MCP Server Handlers", () => {
|
|
|
11
57
|
const invalidArgs = { input: "" };
|
|
12
58
|
const invalidationResult = mockTool.parameters.safeParse(invalidArgs);
|
|
13
59
|
expect(invalidationResult.success).toBe(false);
|
|
14
|
-
|
|
15
|
-
expect(invalidationResult.error.issues[0].message).toContain("cannot be empty");
|
|
16
|
-
}
|
|
60
|
+
expect(invalidationResult.error?.issues[0].message).toContain("cannot be empty");
|
|
17
61
|
});
|
|
18
62
|
it("should handle tool execution errors gracefully", async () => {
|
|
19
63
|
const errorTool = {
|
|
@@ -125,11 +125,8 @@ describe("queryKnowledgeBaseStructured - Basic Functionality", () => {
|
|
|
125
125
|
const result = await queryKnowledgeBaseStructured("kbId", "query", mockClient);
|
|
126
126
|
// resolveToWebsiteUrl should transform S3 URI to website URL
|
|
127
127
|
expect(result[0].location).toBeDefined();
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// URL should have /index removed
|
|
131
|
-
expect(result[0].location.webLocation.url).not.toContain("/index");
|
|
132
|
-
}
|
|
128
|
+
expect(result[0].location?.webLocation?.url).toContain("https://");
|
|
129
|
+
expect(result[0].location?.webLocation?.url).not.toContain("/index");
|
|
133
130
|
});
|
|
134
131
|
it("should skip image content with warning logs", async () => {
|
|
135
132
|
const mockClient = createMockBedrockClient(vi.fn().mockResolvedValue({
|
package/dist/services/bedrock.js
CHANGED
|
@@ -155,7 +155,7 @@ export function resolveToWebsiteUrl(location) {
|
|
|
155
155
|
const match = location.s3Location.uri.match(/^s3:\/\/(?:[^/]+)\/(.+)$/);
|
|
156
156
|
if (match) {
|
|
157
157
|
const key = match[1];
|
|
158
|
-
let url
|
|
158
|
+
let url;
|
|
159
159
|
// Special case: llms-full.txt or llms.txt should be returned as https://dx.pagopa.it/<file>
|
|
160
160
|
if (key === "llms-full.txt" || key === "llms.txt") {
|
|
161
161
|
url = `https://dx.pagopa.it/${key}`;
|
|
@@ -22,18 +22,14 @@ describe("Query Validation", () => {
|
|
|
22
22
|
shortQueries.forEach((input) => {
|
|
23
23
|
const result = queryDocSchema.safeParse(input);
|
|
24
24
|
expect(result.success).toBe(false);
|
|
25
|
-
|
|
26
|
-
expect(result.error.errors[0].message).toBe("Query must be at least 3 characters");
|
|
27
|
-
}
|
|
25
|
+
expect(result.error?.errors[0].message).toBe("Query must be at least 3 characters");
|
|
28
26
|
});
|
|
29
27
|
});
|
|
30
28
|
it("should reject queries exceeding 500 characters", () => {
|
|
31
29
|
const longQuery = { query: "a".repeat(501) };
|
|
32
30
|
const result = queryDocSchema.safeParse(longQuery);
|
|
33
31
|
expect(result.success).toBe(false);
|
|
34
|
-
|
|
35
|
-
expect(result.error.errors[0].message).toBe("Query must not exceed 500 characters");
|
|
36
|
-
}
|
|
32
|
+
expect(result.error?.errors[0].message).toBe("Query must not exceed 500 characters");
|
|
37
33
|
});
|
|
38
34
|
it("should reject missing query field", () => {
|
|
39
35
|
const missingQuery = {};
|
|
@@ -45,23 +45,6 @@ describe("Tool Registry", () => {
|
|
|
45
45
|
expect(entry.tool.annotations.title).toBeTruthy();
|
|
46
46
|
}
|
|
47
47
|
});
|
|
48
|
-
it("should have boolean values for hint annotations when defined", () => {
|
|
49
|
-
for (const entry of toolDefinitions) {
|
|
50
|
-
const { annotations } = entry.tool;
|
|
51
|
-
if (annotations.readOnlyHint !== undefined) {
|
|
52
|
-
expect(annotations.readOnlyHint).toBeTypeOf("boolean");
|
|
53
|
-
}
|
|
54
|
-
if (annotations.destructiveHint !== undefined) {
|
|
55
|
-
expect(annotations.destructiveHint).toBeTypeOf("boolean");
|
|
56
|
-
}
|
|
57
|
-
if (annotations.idempotentHint !== undefined) {
|
|
58
|
-
expect(annotations.idempotentHint).toBeTypeOf("boolean");
|
|
59
|
-
}
|
|
60
|
-
if (annotations.openWorldHint !== undefined) {
|
|
61
|
-
expect(annotations.openWorldHint).toBeTypeOf("boolean");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
48
|
});
|
|
66
49
|
describe("session requirements", () => {
|
|
67
50
|
it("should mark documentation tool as not requiring session", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagopa/dx-mcpserver",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "An MCP server that supports developers using DX tools.",
|
|
6
6
|
"repository": {
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
"axios": "^1.13.6",
|
|
29
29
|
"zod": "^3.25.76",
|
|
30
30
|
"@pagopa/azure-tracing": "^0.4.13",
|
|
31
|
-
"@pagopa/dx-mcpprompts": "^0.2.
|
|
31
|
+
"@pagopa/dx-mcpprompts": "^0.2.5"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.19.15",
|
|
35
35
|
"@vitest/coverage-v8": "^3.2.4",
|
|
36
|
-
"eslint": "^
|
|
36
|
+
"eslint": "^10.1.0",
|
|
37
37
|
"prettier": "3.8.1",
|
|
38
38
|
"tsx": "^4.21.0",
|
|
39
39
|
"typescript": "~5.9.3",
|
|
40
40
|
"vitest": "^3.2.4",
|
|
41
|
-
"@pagopa/eslint-config": "^
|
|
41
|
+
"@pagopa/eslint-config": "^6.0.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"start": "tsx src/cli.ts",
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { vi } from "vitest";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
export const mockTool = {
|
|
4
|
-
annotations: {
|
|
5
|
-
destructiveHint: false,
|
|
6
|
-
idempotentHint: true,
|
|
7
|
-
openWorldHint: true,
|
|
8
|
-
readOnlyHint: true,
|
|
9
|
-
title: "Test Tool",
|
|
10
|
-
},
|
|
11
|
-
description: "A test tool",
|
|
12
|
-
execute: vi.fn(async (args) => {
|
|
13
|
-
const parsedResult = z.object({ input: z.string() }).safeParse(args);
|
|
14
|
-
if (!parsedResult.success) {
|
|
15
|
-
return "Error: Invalid input";
|
|
16
|
-
}
|
|
17
|
-
return `Tool executed with: ${parsedResult.data.input}`;
|
|
18
|
-
}),
|
|
19
|
-
name: "TestTool",
|
|
20
|
-
parameters: z.object({
|
|
21
|
-
input: z.string().min(1, "Input cannot be empty"),
|
|
22
|
-
}),
|
|
23
|
-
};
|
|
24
|
-
export const mockCatalogEntry = {
|
|
25
|
-
category: "test",
|
|
26
|
-
enabled: true,
|
|
27
|
-
id: "test-prompt",
|
|
28
|
-
metadata: {
|
|
29
|
-
description: "A test prompt for unit testing",
|
|
30
|
-
title: "Test Prompt",
|
|
31
|
-
},
|
|
32
|
-
prompt: {
|
|
33
|
-
arguments: [
|
|
34
|
-
{ description: "First argument", name: "arg1", required: true },
|
|
35
|
-
{ description: "Second argument", name: "arg2", required: false },
|
|
36
|
-
],
|
|
37
|
-
description: "A test prompt",
|
|
38
|
-
load: async (args) => `Prompt loaded with args: ${JSON.stringify(args)}`,
|
|
39
|
-
name: "TestPrompt",
|
|
40
|
-
},
|
|
41
|
-
tags: ["test"],
|
|
42
|
-
};
|
|
43
|
-
export const mockPromptEntry = {
|
|
44
|
-
catalogEntry: mockCatalogEntry,
|
|
45
|
-
prompt: {
|
|
46
|
-
load: vi.fn(async (args) => `Prompt loaded with args: ${JSON.stringify(args)}`),
|
|
47
|
-
},
|
|
48
|
-
};
|