@mhalder/qdrant-mcp-server 1.3.1 → 1.4.0
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/CHANGELOG.md +7 -0
- package/README.md +68 -0
- package/build/index.js +71 -6
- package/build/index.js.map +1 -1
- package/build/prompts/index.d.ts +7 -0
- package/build/prompts/index.d.ts.map +1 -0
- package/build/prompts/index.js +7 -0
- package/build/prompts/index.js.map +1 -0
- package/build/prompts/index.test.d.ts +2 -0
- package/build/prompts/index.test.d.ts.map +1 -0
- package/build/prompts/index.test.js +25 -0
- package/build/prompts/index.test.js.map +1 -0
- package/build/prompts/loader.d.ts +25 -0
- package/build/prompts/loader.d.ts.map +1 -0
- package/build/prompts/loader.js +81 -0
- package/build/prompts/loader.js.map +1 -0
- package/build/prompts/loader.test.d.ts +2 -0
- package/build/prompts/loader.test.d.ts.map +1 -0
- package/build/prompts/loader.test.js +417 -0
- package/build/prompts/loader.test.js.map +1 -0
- package/build/prompts/template.d.ts +20 -0
- package/build/prompts/template.d.ts.map +1 -0
- package/build/prompts/template.js +52 -0
- package/build/prompts/template.js.map +1 -0
- package/build/prompts/template.test.d.ts +2 -0
- package/build/prompts/template.test.d.ts.map +1 -0
- package/build/prompts/template.test.js +163 -0
- package/build/prompts/template.test.js.map +1 -0
- package/build/prompts/types.d.ts +34 -0
- package/build/prompts/types.d.ts.map +1 -0
- package/build/prompts/types.js +5 -0
- package/build/prompts/types.js.map +1 -0
- package/package.json +1 -1
- package/prompts.example.json +96 -0
- package/src/index.ts +86 -5
- package/src/prompts/index.test.ts +29 -0
- package/src/prompts/index.ts +7 -0
- package/src/prompts/loader.test.ts +494 -0
- package/src/prompts/loader.ts +90 -0
- package/src/prompts/template.test.ts +212 -0
- package/src/prompts/template.ts +69 -0
- package/src/prompts/types.ts +37 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { renderTemplate, validateArguments } from "./template.js";
|
|
3
|
+
import type { PromptArgument } from "./types.js";
|
|
4
|
+
|
|
5
|
+
describe("renderTemplate", () => {
|
|
6
|
+
it("should render template with provided arguments", () => {
|
|
7
|
+
const template = "Search {{collection}} for {{query}}";
|
|
8
|
+
const args = { collection: "papers", query: "machine learning" };
|
|
9
|
+
const result = renderTemplate(template, args);
|
|
10
|
+
|
|
11
|
+
expect(result.text).toBe("Search papers for machine learning");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should handle multiple occurrences of the same variable", () => {
|
|
15
|
+
const template = "{{name}} said hello. {{name}} said goodbye.";
|
|
16
|
+
const args = { name: "Alice" };
|
|
17
|
+
const result = renderTemplate(template, args);
|
|
18
|
+
|
|
19
|
+
expect(result.text).toBe("Alice said hello. Alice said goodbye.");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should use default values for missing arguments", () => {
|
|
23
|
+
const template = "Return {{limit}} results";
|
|
24
|
+
const args = {};
|
|
25
|
+
const definitions: PromptArgument[] = [
|
|
26
|
+
{ name: "limit", description: "Number of results", required: false, default: "5" },
|
|
27
|
+
];
|
|
28
|
+
const result = renderTemplate(template, args, definitions);
|
|
29
|
+
|
|
30
|
+
expect(result.text).toBe("Return 5 results");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should prefer provided arguments over defaults", () => {
|
|
34
|
+
const template = "Return {{limit}} results";
|
|
35
|
+
const args = { limit: "10" };
|
|
36
|
+
const definitions: PromptArgument[] = [
|
|
37
|
+
{ name: "limit", description: "Number of results", required: false, default: "5" },
|
|
38
|
+
];
|
|
39
|
+
const result = renderTemplate(template, args, definitions);
|
|
40
|
+
|
|
41
|
+
expect(result.text).toBe("Return 10 results");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should keep placeholder if no value and no default", () => {
|
|
45
|
+
const template = "Search {{collection}} for {{query}}";
|
|
46
|
+
const args = { collection: "papers" };
|
|
47
|
+
const result = renderTemplate(template, args);
|
|
48
|
+
|
|
49
|
+
expect(result.text).toBe("Search papers for {{query}}");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should handle templates with no placeholders", () => {
|
|
53
|
+
const template = "This is a plain text template";
|
|
54
|
+
const args = { foo: "bar" };
|
|
55
|
+
const result = renderTemplate(template, args);
|
|
56
|
+
|
|
57
|
+
expect(result.text).toBe("This is a plain text template");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should handle empty template", () => {
|
|
61
|
+
const template = "";
|
|
62
|
+
const args = { foo: "bar" };
|
|
63
|
+
const result = renderTemplate(template, args);
|
|
64
|
+
|
|
65
|
+
expect(result.text).toBe("");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should handle empty args", () => {
|
|
69
|
+
const template = "Search {{collection}}";
|
|
70
|
+
const result = renderTemplate(template);
|
|
71
|
+
|
|
72
|
+
expect(result.text).toBe("Search {{collection}}");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should handle complex template with multiple variables and defaults", () => {
|
|
76
|
+
const template =
|
|
77
|
+
"Search the '{{collection}}' collection for documents similar to: '{{query}}'. Return the top {{limit}} most relevant results.";
|
|
78
|
+
const args = { collection: "papers", query: "neural networks" };
|
|
79
|
+
const definitions: PromptArgument[] = [
|
|
80
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
81
|
+
{ name: "query", description: "Search query", required: true },
|
|
82
|
+
{ name: "limit", description: "Number of results", required: false, default: "5" },
|
|
83
|
+
];
|
|
84
|
+
const result = renderTemplate(template, args, definitions);
|
|
85
|
+
|
|
86
|
+
expect(result.text).toBe(
|
|
87
|
+
"Search the 'papers' collection for documents similar to: 'neural networks'. Return the top 5 most relevant results."
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should handle special characters in argument values", () => {
|
|
92
|
+
const template = "Query: {{query}}";
|
|
93
|
+
const args = { query: "What is $100 + $200?" };
|
|
94
|
+
const result = renderTemplate(template, args);
|
|
95
|
+
|
|
96
|
+
expect(result.text).toBe("Query: What is $100 + $200?");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should handle numeric-like values as strings", () => {
|
|
100
|
+
const template = "ID: {{id}}, Count: {{count}}";
|
|
101
|
+
const args = { id: "123", count: "456" };
|
|
102
|
+
const result = renderTemplate(template, args);
|
|
103
|
+
|
|
104
|
+
expect(result.text).toBe("ID: 123, Count: 456");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should handle whitespace in variable names", () => {
|
|
108
|
+
const template = "Value: {{key}}";
|
|
109
|
+
const args = { key: " spaced " };
|
|
110
|
+
const result = renderTemplate(template, args);
|
|
111
|
+
|
|
112
|
+
expect(result.text).toBe("Value: spaced ");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should not match invalid placeholder patterns", () => {
|
|
116
|
+
const template = "{{ invalid }} {missing} {{also invalid}} {{valid}}";
|
|
117
|
+
const args = { valid: "yes" };
|
|
118
|
+
const result = renderTemplate(template, args);
|
|
119
|
+
|
|
120
|
+
// Only {{valid}} should be replaced, others don't match \w+ pattern
|
|
121
|
+
expect(result.text).toBe("{{ invalid }} {missing} {{also invalid}} yes");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("validateArguments", () => {
|
|
126
|
+
it("should pass validation when all required arguments are provided", () => {
|
|
127
|
+
const args = { collection: "papers", query: "test" };
|
|
128
|
+
const definitions: PromptArgument[] = [
|
|
129
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
130
|
+
{ name: "query", description: "Search query", required: true },
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
expect(() => validateArguments(args, definitions)).not.toThrow();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should throw error when required argument is missing", () => {
|
|
137
|
+
const args = { collection: "papers" };
|
|
138
|
+
const definitions: PromptArgument[] = [
|
|
139
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
140
|
+
{ name: "query", description: "Search query", required: true },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
expect(() => validateArguments(args, definitions)).toThrow("Missing required arguments: query");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should throw error when required argument is empty string", () => {
|
|
147
|
+
const args = { collection: "papers", query: "" };
|
|
148
|
+
const definitions: PromptArgument[] = [
|
|
149
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
150
|
+
{ name: "query", description: "Search query", required: true },
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
expect(() => validateArguments(args, definitions)).toThrow("Missing required arguments: query");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should throw error listing multiple missing arguments", () => {
|
|
157
|
+
const args = {};
|
|
158
|
+
const definitions: PromptArgument[] = [
|
|
159
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
160
|
+
{ name: "query", description: "Search query", required: true },
|
|
161
|
+
{ name: "filter", description: "Filter", required: true },
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
expect(() => validateArguments(args, definitions)).toThrow(
|
|
165
|
+
"Missing required arguments: collection, query, filter"
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("should not throw for missing optional arguments", () => {
|
|
170
|
+
const args = { collection: "papers", query: "test" };
|
|
171
|
+
const definitions: PromptArgument[] = [
|
|
172
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
173
|
+
{ name: "query", description: "Search query", required: true },
|
|
174
|
+
{ name: "limit", description: "Limit", required: false, default: "5" },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
expect(() => validateArguments(args, definitions)).not.toThrow();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("should pass validation with empty definitions", () => {
|
|
181
|
+
const args = { foo: "bar" };
|
|
182
|
+
const definitions: PromptArgument[] = [];
|
|
183
|
+
|
|
184
|
+
expect(() => validateArguments(args, definitions)).not.toThrow();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("should pass validation with no definitions", () => {
|
|
188
|
+
const args = { foo: "bar" };
|
|
189
|
+
|
|
190
|
+
expect(() => validateArguments(args, [])).not.toThrow();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("should not require arguments that are not defined", () => {
|
|
194
|
+
const args = { collection: "papers", extra: "data" };
|
|
195
|
+
const definitions: PromptArgument[] = [
|
|
196
|
+
{ name: "collection", description: "Collection name", required: true },
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
expect(() => validateArguments(args, definitions)).not.toThrow();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("should handle mixed required and optional arguments", () => {
|
|
203
|
+
const args = { name: "test" };
|
|
204
|
+
const definitions: PromptArgument[] = [
|
|
205
|
+
{ name: "name", description: "Name", required: true },
|
|
206
|
+
{ name: "limit", description: "Limit", required: false },
|
|
207
|
+
{ name: "filter", description: "Filter", required: false },
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
expect(() => validateArguments(args, definitions)).not.toThrow();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple template rendering engine for prompts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { PromptArgument, RenderedPrompt } from "./types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Render a template string by replacing {{variable}} placeholders with actual values
|
|
9
|
+
* @param template Template string with {{variable}} placeholders
|
|
10
|
+
* @param args Record of argument values
|
|
11
|
+
* @param definitions Argument definitions with defaults
|
|
12
|
+
* @returns Rendered template
|
|
13
|
+
*/
|
|
14
|
+
export function renderTemplate(
|
|
15
|
+
template: string,
|
|
16
|
+
args: Record<string, string> = {},
|
|
17
|
+
definitions: PromptArgument[] = []
|
|
18
|
+
): RenderedPrompt {
|
|
19
|
+
let rendered = template;
|
|
20
|
+
|
|
21
|
+
// Create a map of defaults
|
|
22
|
+
const defaults = new Map<string, string>();
|
|
23
|
+
for (const def of definitions) {
|
|
24
|
+
if (def.default !== undefined) {
|
|
25
|
+
defaults.set(def.name, def.default);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Replace all {{variable}} placeholders
|
|
30
|
+
rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
|
|
31
|
+
// Check if value is provided in args
|
|
32
|
+
if (args[varName] !== undefined) {
|
|
33
|
+
return args[varName];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check if there's a default value
|
|
37
|
+
if (defaults.has(varName)) {
|
|
38
|
+
return defaults.get(varName)!;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// If no value and no default, keep the placeholder
|
|
42
|
+
return match;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return { text: rendered };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate that all required arguments are provided
|
|
50
|
+
* @param args Provided arguments
|
|
51
|
+
* @param definitions Argument definitions
|
|
52
|
+
* @throws Error if required arguments are missing
|
|
53
|
+
*/
|
|
54
|
+
export function validateArguments(
|
|
55
|
+
args: Record<string, string>,
|
|
56
|
+
definitions: PromptArgument[]
|
|
57
|
+
): void {
|
|
58
|
+
const missing: string[] = [];
|
|
59
|
+
|
|
60
|
+
for (const def of definitions) {
|
|
61
|
+
if (def.required && (args[def.name] === undefined || args[def.name] === "")) {
|
|
62
|
+
missing.push(def.name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (missing.length > 0) {
|
|
67
|
+
throw new Error(`Missing required arguments: ${missing.join(", ")}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types for MCP prompts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Argument definition for a prompt
|
|
7
|
+
*/
|
|
8
|
+
export interface PromptArgument {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
required: boolean;
|
|
12
|
+
default?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Individual prompt definition
|
|
17
|
+
*/
|
|
18
|
+
export interface PromptDefinition {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
arguments: PromptArgument[];
|
|
22
|
+
template: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Root configuration structure
|
|
27
|
+
*/
|
|
28
|
+
export interface PromptsConfig {
|
|
29
|
+
prompts: PromptDefinition[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parsed template with resolved arguments
|
|
34
|
+
*/
|
|
35
|
+
export interface RenderedPrompt {
|
|
36
|
+
text: string;
|
|
37
|
+
}
|