@promptlayer/mcp-server 1.0.0 → 1.2.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/README.md +18 -0
- package/build/client.d.ts +4 -0
- package/build/client.d.ts.map +1 -1
- package/build/client.js +5 -0
- package/build/client.js.map +1 -1
- package/build/handlers.d.ts.map +1 -1
- package/build/handlers.js +5 -0
- package/build/handlers.js.map +1 -1
- package/build/types.d.ts +272 -0
- package/build/types.d.ts.map +1 -1
- package/build/types.js +111 -1
- package/build/types.js.map +1 -1
- package/gcp/.gcloudignore +4 -0
- package/gcp/app.yaml +2 -0
- package/gcp/package-lock.json +2700 -0
- package/gcp/package.json +26 -0
- package/gcp/src/index.ts +191 -0
- package/gcp/tsconfig.json +15 -0
- package/package.json +1 -1
- package/scripts/diff-endpoints.ts +27 -0
- package/scripts/extract-mcp-tools.ts +4 -0
- package/src/client.ts +6 -0
- package/src/handlers.ts +13 -0
- package/src/types.ts +125 -1
- package/.claude/settings.local.json +0 -16
package/gcp/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "promptlayer-mcp-gcp",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "esbuild src/index.ts --bundle --platform=node --format=esm --outfile=build/server.mjs --packages=external",
|
|
7
|
+
"gcp-build": "",
|
|
8
|
+
"start": "node build/server.mjs",
|
|
9
|
+
"dev": "tsx src/index.ts"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
13
|
+
"express": "^4.21.0",
|
|
14
|
+
"zod": "^3.25.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/express": "^4.17.21",
|
|
18
|
+
"@types/node": "^22.15.0",
|
|
19
|
+
"esbuild": "^0.25.0",
|
|
20
|
+
"tsx": "^4.20.0",
|
|
21
|
+
"typescript": "^5.7.0"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/gcp/src/index.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { TOOL_DEFINITIONS } from "../../src/types.js";
|
|
5
|
+
import { PromptLayerClient } from "../../src/client.js";
|
|
6
|
+
|
|
7
|
+
// ── Tool handler mapping ────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
type Args = Record<string, unknown>;
|
|
10
|
+
type ToolHandler = (client: PromptLayerClient, args: Args) => Promise<unknown>;
|
|
11
|
+
|
|
12
|
+
function body(args: Args): Args {
|
|
13
|
+
const { api_key: _, ...rest } = args;
|
|
14
|
+
return rest;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TOOL_HANDLERS: Record<string, ToolHandler> = {
|
|
18
|
+
// Prompt Templates
|
|
19
|
+
"get-prompt-template": (c, { api_key: _, prompt_name, ...p }) =>
|
|
20
|
+
c.getPromptTemplate(prompt_name as string, p),
|
|
21
|
+
"get-prompt-template-raw": (c, { api_key: _, identifier, ...p }) =>
|
|
22
|
+
c.getPromptTemplateRaw(identifier as string, p),
|
|
23
|
+
"list-prompt-templates": (c, a) => c.listPromptTemplates(body(a)),
|
|
24
|
+
"publish-prompt-template": (c, a) => c.publishPromptTemplate(body(a)),
|
|
25
|
+
"list-prompt-template-labels": (c, { identifier }) =>
|
|
26
|
+
c.listPromptTemplateLabels(identifier as string),
|
|
27
|
+
"create-prompt-label": (c, { api_key: _, prompt_id, ...b }) =>
|
|
28
|
+
c.createPromptLabel(prompt_id as number, b),
|
|
29
|
+
"move-prompt-label": (c, { api_key: _, prompt_label_id, ...b }) =>
|
|
30
|
+
c.movePromptLabel(prompt_label_id as number, b),
|
|
31
|
+
"delete-prompt-label": (c, { prompt_label_id }) =>
|
|
32
|
+
c.deletePromptLabel(prompt_label_id as number),
|
|
33
|
+
"get-snippet-usage": (c, { api_key: _, identifier, ...p }) =>
|
|
34
|
+
c.getSnippetUsage(identifier as string, p),
|
|
35
|
+
|
|
36
|
+
// Request Logs
|
|
37
|
+
"search-request-logs": (c, a) => c.searchRequestLogs(body(a)),
|
|
38
|
+
"get-request": (c, { request_id }) => c.getRequest(request_id as number),
|
|
39
|
+
|
|
40
|
+
// Tracking
|
|
41
|
+
"log-request": (c, a) => c.logRequest(body(a)),
|
|
42
|
+
"create-spans-bulk": (c, a) => c.createSpansBulk(body(a)),
|
|
43
|
+
|
|
44
|
+
// Datasets
|
|
45
|
+
"list-datasets": (c, a) => c.listDatasets(body(a)),
|
|
46
|
+
"create-dataset-group": (c, a) => c.createDatasetGroup(body(a)),
|
|
47
|
+
"create-dataset-version-from-file": (c, a) => c.createDatasetVersionFromFile(body(a)),
|
|
48
|
+
"create-dataset-version-from-filter-params": (c, a) => c.createDatasetVersionFromFilterParams(body(a)),
|
|
49
|
+
"get-dataset-rows": (c, { api_key: _, dataset_id, ...p }) =>
|
|
50
|
+
c.getDatasetRows(dataset_id as number, p),
|
|
51
|
+
|
|
52
|
+
// Evaluations
|
|
53
|
+
"list-evaluations": (c, a) => c.listEvaluations(body(a)),
|
|
54
|
+
"get-evaluation-rows": (c, { api_key: _, evaluation_id, ...p }) =>
|
|
55
|
+
c.getEvaluationRows(evaluation_id as number, p),
|
|
56
|
+
"create-report": (c, a) => c.createReport(body(a)),
|
|
57
|
+
"run-report": (c, { api_key: _, report_id, ...b }) =>
|
|
58
|
+
c.runReport(report_id as number, b),
|
|
59
|
+
"get-report": (c, { report_id }) => c.getReport(report_id as number),
|
|
60
|
+
"get-report-score": (c, { report_id }) => c.getReportScore(report_id as number),
|
|
61
|
+
"update-report-score-card": (c, { api_key: _, report_id, ...b }) =>
|
|
62
|
+
c.updateReportScoreCard(report_id as number, b),
|
|
63
|
+
"delete-reports-by-name": (c, { report_name }) =>
|
|
64
|
+
c.deleteReportsByName(report_name as string),
|
|
65
|
+
|
|
66
|
+
// Agents
|
|
67
|
+
"list-workflows": (c, a) => c.listWorkflows(body(a)),
|
|
68
|
+
"create-workflow": (c, a) => c.createWorkflow(body(a)),
|
|
69
|
+
"patch-workflow": (c, { api_key: _, workflow_id_or_name, ...b }) =>
|
|
70
|
+
c.patchWorkflow(workflow_id_or_name as string, b),
|
|
71
|
+
"run-workflow": (c, { api_key: _, workflow_name, ...b }) =>
|
|
72
|
+
c.runWorkflow(workflow_name as string, b),
|
|
73
|
+
"get-workflow-version-execution-results": (c, a) =>
|
|
74
|
+
c.getWorkflowVersionExecutionResults(body(a)),
|
|
75
|
+
"get-workflow": (c, { workflow_id_or_name }) =>
|
|
76
|
+
c.getWorkflow(workflow_id_or_name as string),
|
|
77
|
+
|
|
78
|
+
// Folders
|
|
79
|
+
"create-folder": (c, a) => c.createFolder(body(a)),
|
|
80
|
+
"edit-folder": (c, { api_key: _, folder_id, ...b }) =>
|
|
81
|
+
c.editFolder(folder_id as number, b),
|
|
82
|
+
"get-folder-entities": (c, a) => c.getFolderEntities(body(a)),
|
|
83
|
+
"move-folder-entities": (c, a) => c.moveFolderEntities(body(a)),
|
|
84
|
+
"delete-folder-entities": (c, a) => c.deleteFolderEntities(body(a)),
|
|
85
|
+
"resolve-folder-id": (c, a) => c.resolveFolderId(body(a)),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// ── Instructions ────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
const INSTRUCTIONS = `
|
|
91
|
+
PromptLayer is a prompt management and observability platform. This MCP server lets you manage PromptLayer resources.
|
|
92
|
+
|
|
93
|
+
## Key entities and naming
|
|
94
|
+
|
|
95
|
+
- **Prompt template**: A versioned prompt in the registry. Each version has a prompt_template (the content) and metadata. Versions are immutable — publishing always creates a new version.
|
|
96
|
+
- **Snippet**: A reusable prompt fragment referenced inside prompt templates with @@@snippet_name@@@ markers. Snippets are themselves prompt templates (with type "completion"). When a prompt is fetched, snippets are expanded inline by default.
|
|
97
|
+
- **Release label**: A pointer (e.g. "prod", "staging") attached to a specific prompt version. Move labels between versions for deployment.
|
|
98
|
+
- **Agent** (backend name: workflow): A multi-step pipeline of nodes. Each node has a type, configuration, and dependencies. Agents are versioned like prompts.
|
|
99
|
+
- **Evaluation pipeline** (backend name: report): Runs evaluation columns against a dataset and produces scores. Columns can be LLM assertions, code execution, comparisons, etc.
|
|
100
|
+
- **Dataset**: A versioned collection of test rows. Belongs to a dataset group. Versions can be created from CSV/JSON files or by filtering request log history.
|
|
101
|
+
- **Folder**: Organizes prompts, agents, datasets, evaluations, and other entities into a hierarchy.
|
|
102
|
+
|
|
103
|
+
## Working with prompts and snippets
|
|
104
|
+
|
|
105
|
+
When editing a prompt that may contain snippets, always use get-prompt-template-raw with resolve_snippets=false. This preserves the raw @@@snippet_name@@@ references so they are not lost on re-publish. The response also includes a "snippets" array listing every snippet used.
|
|
106
|
+
|
|
107
|
+
When publishing back, keep @@@snippet_name@@@ markers intact in the prompt_template content. Do not inline snippet text — this breaks the snippet reference and future snippet updates will no longer propagate.
|
|
108
|
+
|
|
109
|
+
Use get-prompt-template (the POST variant) only when you need a fully rendered prompt ready to send to an LLM, with input_variables filled in and provider-specific formatting applied.
|
|
110
|
+
|
|
111
|
+
## Working with evaluations
|
|
112
|
+
|
|
113
|
+
The recommended way to create an evaluation pipeline is with LLM assertion columns — these use a language model to score each dataset row. For details on all available column types (LLM assertion, code execution, comparison, etc.), search the PromptLayer docs or see https://docs.promptlayer.com/features/evaluations/column-types.
|
|
114
|
+
|
|
115
|
+
## Additional documentation
|
|
116
|
+
|
|
117
|
+
For deeper questions about PromptLayer features, configuration, or API details, the PromptLayer docs site has an MCP server you can use for search. See https://docs.promptlayer.com/mcp for setup.
|
|
118
|
+
`.trim();
|
|
119
|
+
|
|
120
|
+
function resolveApiKey(argKey?: string, headerKey?: string): string {
|
|
121
|
+
const key = argKey || headerKey;
|
|
122
|
+
if (!key) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"No API key provided. Set authorization_token in your MCP config " +
|
|
125
|
+
"or pass api_key to each tool call.",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
if (!key.startsWith("pl_")) {
|
|
129
|
+
throw new Error("Invalid API key format. PromptLayer API keys must start with 'pl_'.");
|
|
130
|
+
}
|
|
131
|
+
return key;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function createMcpServer(defaultApiKey?: string): McpServer {
|
|
135
|
+
const server = new McpServer(
|
|
136
|
+
{ name: "promptlayer-server", version: "1.0.0" },
|
|
137
|
+
{ instructions: INSTRUCTIONS },
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
for (const [name, def] of Object.entries(TOOL_DEFINITIONS)) {
|
|
141
|
+
const handler = TOOL_HANDLERS[name];
|
|
142
|
+
if (!handler) continue;
|
|
143
|
+
|
|
144
|
+
server.tool(name, def.description, def.inputSchema.shape, async (args: Args) => {
|
|
145
|
+
try {
|
|
146
|
+
const apiKey = resolveApiKey(args.api_key as string | undefined, defaultApiKey);
|
|
147
|
+
const client = new PromptLayerClient(apiKey);
|
|
148
|
+
const result = await handler(client, args);
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
151
|
+
};
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return {
|
|
154
|
+
content: [{
|
|
155
|
+
type: "text" as const,
|
|
156
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
157
|
+
}],
|
|
158
|
+
isError: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return server;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Express app ─────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
const app = express();
|
|
170
|
+
app.use(express.json());
|
|
171
|
+
|
|
172
|
+
async function handleMcp(req: express.Request, res: express.Response) {
|
|
173
|
+
const auth = req.headers.authorization;
|
|
174
|
+
const apiKey = auth?.startsWith("Bearer ") ? auth.slice(7) : undefined;
|
|
175
|
+
|
|
176
|
+
const transport = new StreamableHTTPServerTransport({
|
|
177
|
+
sessionIdGenerator: undefined,
|
|
178
|
+
});
|
|
179
|
+
const server = createMcpServer(apiKey);
|
|
180
|
+
await server.connect(transport);
|
|
181
|
+
await transport.handleRequest(req, res, req.body);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
app.post("/mcp", handleMcp);
|
|
185
|
+
app.get("/mcp", handleMcp);
|
|
186
|
+
app.delete("/mcp", handleMcp);
|
|
187
|
+
|
|
188
|
+
const PORT = parseInt(process.env.PORT || "8080");
|
|
189
|
+
app.listen(PORT, () => {
|
|
190
|
+
console.log(`PromptLayer MCP server listening on port ${PORT}`);
|
|
191
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["es2022"],
|
|
7
|
+
"noEmit": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*.ts"]
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -105,6 +105,33 @@ const KNOWN_EXCEPTIONS: string[] = [
|
|
|
105
105
|
// The OpenAPI spec defines scores as object but backend uses a list of {name, operator, value}.
|
|
106
106
|
// Our array type matches what the backend actually accepts.
|
|
107
107
|
'TYPE MISMATCH: POST /api/public/v2/dataset-versions/from-filter-params > "scores": MCP=array, OpenAPI=object [tool: create-dataset-version-from-filter-params]',
|
|
108
|
+
|
|
109
|
+
// patch-prompt-template-version exists in the backend and OpenAPI but is not yet implemented
|
|
110
|
+
// in MCP. Low priority — publish-prompt-template covers most use cases.
|
|
111
|
+
"MISSING IN MCP: PATCH /rest/prompt-templates/{identifier} (Patch Prompt Template Version)",
|
|
112
|
+
|
|
113
|
+
// OTLP trace ingestion endpoint exists in OpenAPI but is not implemented in MCP.
|
|
114
|
+
// This is a specialized protocol endpoint, not a typical REST API tool.
|
|
115
|
+
"MISSING IN MCP: POST /v1/traces (Ingest Traces (OTLP))",
|
|
116
|
+
|
|
117
|
+
// Semantic search and tags parameters for folder entities are documented in the reference docs
|
|
118
|
+
// (https://docs.promptlayer.com/reference/list-folder-entities) but not yet in the OpenAPI spec.
|
|
119
|
+
'EXTRA FIELD: GET /api/public/v2/folders/entities > "semantic_search" not in OpenAPI [tool: get-folder-entities]',
|
|
120
|
+
'EXTRA FIELD: GET /api/public/v2/folders/entities > "semantic_search_top_k" not in OpenAPI [tool: get-folder-entities]',
|
|
121
|
+
'EXTRA FIELD: GET /api/public/v2/folders/entities > "semantic_search_threshold" not in OpenAPI [tool: get-folder-entities]',
|
|
122
|
+
'EXTRA FIELD: GET /api/public/v2/folders/entities > "tags" not in OpenAPI [tool: get-folder-entities]',
|
|
123
|
+
|
|
124
|
+
// The OpenAPI spec represents tags as anyOf(string, array) but our Zod union serializes
|
|
125
|
+
// as "union" vs "union(string,array)". Functionally equivalent.
|
|
126
|
+
'TYPE MISMATCH: GET /prompt-templates > "tags": MCP=union, OpenAPI=union(string,array) [tool: list-prompt-templates]',
|
|
127
|
+
|
|
128
|
+
// filter_type OpenAPI uses oneOf vs our Zod union. Functionally equivalent.
|
|
129
|
+
'TYPE MISMATCH: GET /api/public/v2/folders/entities > "filter_type": MCP=union, OpenAPI=oneOf [tool: get-folder-entities]',
|
|
130
|
+
|
|
131
|
+
// workspace_id is optional in MCP because the API key implicitly provides it.
|
|
132
|
+
// OpenAPI marks it required but the backend falls back to the API key's workspace.
|
|
133
|
+
'REQUIRED MISMATCH: GET /api/public/v2/folders/entities > "workspace_id": MCP=false, OpenAPI=true [tool: get-folder-entities]',
|
|
134
|
+
'REQUIRED MISMATCH: GET /api/public/v2/folders/resolve-id > "workspace_id": MCP=false, OpenAPI=true [tool: resolve-folder-id]',
|
|
108
135
|
];
|
|
109
136
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
110
137
|
|
|
@@ -28,13 +28,17 @@ const TOOL_TO_ENDPOINT: Record<string, { method: string; path: string }> = {
|
|
|
28
28
|
"move-prompt-label": { method: "PATCH", path: "/prompt-labels/{prompt_label_id}" },
|
|
29
29
|
"delete-prompt-label": { method: "DELETE", path: "/prompt-labels/{prompt_label_id}" },
|
|
30
30
|
"get-snippet-usage": { method: "GET", path: "/prompt-templates/{identifier}/snippet-usage" },
|
|
31
|
+
"search-request-logs": { method: "POST", path: "/api/public/v2/requests/search" },
|
|
32
|
+
"get-request": { method: "GET", path: "/api/public/v2/requests/{request_id}" },
|
|
31
33
|
"log-request": { method: "POST", path: "/log-request" },
|
|
32
34
|
"create-spans-bulk": { method: "POST", path: "/spans-bulk" },
|
|
33
35
|
"list-datasets": { method: "GET", path: "/api/public/v2/datasets" },
|
|
36
|
+
"get-dataset-rows": { method: "GET", path: "/api/public/v2/datasets/{dataset_id}/rows" },
|
|
34
37
|
"create-dataset-group": { method: "POST", path: "/api/public/v2/dataset-groups" },
|
|
35
38
|
"create-dataset-version-from-file": { method: "POST", path: "/api/public/v2/dataset-versions/from-file" },
|
|
36
39
|
"create-dataset-version-from-filter-params": { method: "POST", path: "/api/public/v2/dataset-versions/from-filter-params" },
|
|
37
40
|
"list-evaluations": { method: "GET", path: "/api/public/v2/evaluations" },
|
|
41
|
+
"get-evaluation-rows": { method: "GET", path: "/api/public/v2/evaluations/{evaluation_id}/rows" },
|
|
38
42
|
"create-report": { method: "POST", path: "/reports" },
|
|
39
43
|
"run-report": { method: "POST", path: "/reports/{report_id}/run" },
|
|
40
44
|
"get-report": { method: "GET", path: "/reports/{report_id}" },
|
package/src/client.ts
CHANGED
|
@@ -55,18 +55,24 @@ export class PromptLayerClient {
|
|
|
55
55
|
deletePromptLabel(labelId: number) { return this.del(`/prompt-labels/${labelId}`); }
|
|
56
56
|
getSnippetUsage(id: string, params?: Body) { return this.get(`/prompt-templates/${this.enc(id)}/snippet-usage`, params); }
|
|
57
57
|
|
|
58
|
+
// Request Logs
|
|
59
|
+
searchRequestLogs(body: Body) { return this.post("/api/public/v2/requests/search", body); }
|
|
60
|
+
getRequest(requestId: number) { return this.get(`/api/public/v2/requests/${requestId}`); }
|
|
61
|
+
|
|
58
62
|
// Tracking
|
|
59
63
|
logRequest(body: Body) { return this.post("/log-request", body); }
|
|
60
64
|
createSpansBulk(body: Body) { return this.post("/spans-bulk", body); }
|
|
61
65
|
|
|
62
66
|
// Datasets
|
|
63
67
|
listDatasets(params?: Body) { return this.get("/api/public/v2/datasets", params); }
|
|
68
|
+
getDatasetRows(id: number, params?: Body) { return this.get(`/api/public/v2/datasets/${id}/rows`, params); }
|
|
64
69
|
createDatasetGroup(body: Body) { return this.post("/api/public/v2/dataset-groups", body); }
|
|
65
70
|
createDatasetVersionFromFile(body: Body) { return this.post("/api/public/v2/dataset-versions/from-file", body); }
|
|
66
71
|
createDatasetVersionFromFilterParams(body: Body) { return this.post("/api/public/v2/dataset-versions/from-filter-params", body); }
|
|
67
72
|
|
|
68
73
|
// Evaluations
|
|
69
74
|
listEvaluations(params?: Body) { return this.get("/api/public/v2/evaluations", params); }
|
|
75
|
+
getEvaluationRows(id: number, params?: Body) { return this.get(`/api/public/v2/evaluations/${id}/rows`, params); }
|
|
70
76
|
createReport(body: Body) { return this.post("/reports", body); }
|
|
71
77
|
runReport(id: number, body: Body) { return this.post(`/reports/${id}/run`, body); }
|
|
72
78
|
getReport(id: number) { return this.get(`/reports/${id}`); }
|
package/src/handlers.ts
CHANGED
|
@@ -50,6 +50,13 @@ export function registerAllTools(server: any) {
|
|
|
50
50
|
(c, a) => { const { api_key: _, identifier, ...p } = a as { identifier: string; api_key?: string } & Args; return c.getSnippetUsage(identifier, p); },
|
|
51
51
|
(r) => `${Array.isArray(r) ? r.length : 0} prompt(s) using snippet`);
|
|
52
52
|
|
|
53
|
+
// Request Logs
|
|
54
|
+
reg(t["search-request-logs"], (c, a) => c.searchRequestLogs(body(a)),
|
|
55
|
+
(r) => { const { items, total, page, pages } = r as { items?: unknown[]; total?: number; page?: number; pages?: number }; return `${items?.length ?? 0} request(s) (page ${page ?? 1}/${pages ?? 1}, total ${total ?? "?"})`; });
|
|
56
|
+
reg(t["get-request"],
|
|
57
|
+
(c, a) => c.getRequest((a as { request_id: number }).request_id),
|
|
58
|
+
(r) => { const { request_id, model } = r as { request_id?: number; model?: string }; return `Request ${request_id ?? ""}${model ? ` (${model})` : ""} retrieved`; });
|
|
59
|
+
|
|
53
60
|
// Tracking
|
|
54
61
|
reg(t["log-request"], (c, a) => c.logRequest(body(a)),
|
|
55
62
|
(r) => { const id = (r as { request_id?: unknown }).request_id; return id ? `Logged (ID: ${id})` : "Logged"; });
|
|
@@ -62,10 +69,16 @@ export function registerAllTools(server: any) {
|
|
|
62
69
|
reg(t["create-dataset-group"], (c, a) => c.createDatasetGroup(body(a)), () => "Dataset group created");
|
|
63
70
|
reg(t["create-dataset-version-from-file"], (c, a) => c.createDatasetVersionFromFile(body(a)), () => "Dataset version from file initiated");
|
|
64
71
|
reg(t["create-dataset-version-from-filter-params"], (c, a) => c.createDatasetVersionFromFilterParams(body(a)), () => "Dataset version from history initiated");
|
|
72
|
+
reg(t["get-dataset-rows"],
|
|
73
|
+
(c, a) => { const { api_key: _, dataset_id, ...p } = a as { dataset_id: number; api_key?: string } & Args; return c.getDatasetRows(dataset_id, p); },
|
|
74
|
+
(r) => { const { rows, total, page, pages } = r as { rows?: unknown[]; total?: number; page?: number; pages?: number }; return `${rows?.length ?? 0} row(s) (page ${page ?? 1}/${pages ?? 1}, total ${total ?? "?"})`; });
|
|
65
75
|
|
|
66
76
|
// Evaluations
|
|
67
77
|
reg(t["list-evaluations"], (c, a) => c.listEvaluations(body(a)),
|
|
68
78
|
(r) => { const { items, total } = r as { items?: unknown[]; total?: number }; return `${items?.length ?? 0} evaluation(s) (total: ${total ?? "?"})`; });
|
|
79
|
+
reg(t["get-evaluation-rows"],
|
|
80
|
+
(c, a) => { const { api_key: _, evaluation_id, ...p } = a as { evaluation_id: number; api_key?: string } & Args; return c.getEvaluationRows(evaluation_id, p); },
|
|
81
|
+
(r) => { const { rows, total, page, pages } = r as { rows?: unknown[]; total?: number; page?: number; pages?: number }; return `${rows?.length ?? 0} row(s) (page ${page ?? 1}/${pages ?? 1}, total ${total ?? "?"})`; });
|
|
69
82
|
reg(t["create-report"], (c, a) => c.createReport(body(a)),
|
|
70
83
|
(r) => { const id = (r as { report_id?: number }).report_id; return id ? `Pipeline created (ID: ${id})` : "Pipeline created"; });
|
|
71
84
|
reg(t["run-report"],
|
package/src/types.ts
CHANGED
|
@@ -71,6 +71,7 @@ export const ListPromptTemplatesArgsSchema = z.object({
|
|
|
71
71
|
per_page: z.number().int().optional().describe("Items per page"),
|
|
72
72
|
label: z.string().optional().describe("Filter by release label"),
|
|
73
73
|
name: z.string().optional().describe("Filter by name (case-insensitive partial match)"),
|
|
74
|
+
tags: z.union([z.string(), z.array(z.string())]).optional().describe("Filter by tag(s). Only templates whose tags contain all specified values are returned."),
|
|
74
75
|
status: z.enum(["active", "deleted", "all"]).optional().describe("Filter by status (default: 'active')"),
|
|
75
76
|
workspace_id: z.number().int().optional().describe("Workspace ID"),
|
|
76
77
|
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
@@ -268,6 +269,17 @@ export const CreateDatasetVersionFromFilterParamsArgsSchema = z.object({
|
|
|
268
269
|
});
|
|
269
270
|
|
|
270
271
|
|
|
272
|
+
// ── Get Dataset Rows (GET /api/public/v2/datasets/{dataset_id}/rows) ─────
|
|
273
|
+
|
|
274
|
+
export const GetDatasetRowsArgsSchema = z.object({
|
|
275
|
+
dataset_id: z.number().int().describe("The ID of the dataset to retrieve rows from"),
|
|
276
|
+
workspace_id: z.number().int().optional().describe("Filter by workspace ID"),
|
|
277
|
+
page: z.number().int().optional().describe("Page number (default: 1)"),
|
|
278
|
+
per_page: z.number().int().optional().describe("Rows per page (default: 10, max: 100)"),
|
|
279
|
+
q: z.string().optional().describe("Search query for filtering rows"),
|
|
280
|
+
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
281
|
+
});
|
|
282
|
+
|
|
271
283
|
// ── List Evaluations (GET /api/public/v2/evaluations) ────────────────────
|
|
272
284
|
|
|
273
285
|
export const ListEvaluationsArgsSchema = z.object({
|
|
@@ -279,6 +291,16 @@ export const ListEvaluationsArgsSchema = z.object({
|
|
|
279
291
|
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
280
292
|
});
|
|
281
293
|
|
|
294
|
+
// ── Get Evaluation Rows (GET /api/public/v2/evaluations/{evaluation_id}/rows)
|
|
295
|
+
|
|
296
|
+
export const GetEvaluationRowsArgsSchema = z.object({
|
|
297
|
+
evaluation_id: z.number().int().describe("The ID of the evaluation to retrieve rows from"),
|
|
298
|
+
workspace_id: z.number().int().optional().describe("Filter by workspace ID"),
|
|
299
|
+
page: z.number().int().optional().describe("Page number (default: 1)"),
|
|
300
|
+
per_page: z.number().int().optional().describe("Rows per page (default: 10, max: 100)"),
|
|
301
|
+
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
302
|
+
});
|
|
303
|
+
|
|
282
304
|
// ── Create Evaluation Pipeline / Report (POST /reports) ──────────────────
|
|
283
305
|
|
|
284
306
|
export const CreateReportArgsSchema = z.object({
|
|
@@ -434,7 +456,11 @@ const EntityTypeEnum = z.enum([
|
|
|
434
456
|
export const GetFolderEntitiesArgsSchema = z.object({
|
|
435
457
|
folder_id: z.number().int().optional().describe("Folder ID to list (root if omitted)"),
|
|
436
458
|
filter_type: z.union([EntityTypeEnum, z.array(EntityTypeEnum)]).optional().describe("Entity type(s) to include (default: all)"),
|
|
437
|
-
search_query: z.string().optional().describe("Search by name (case-insensitive partial match)"),
|
|
459
|
+
search_query: z.string().optional().describe("Search by name (case-insensitive partial match). For prompts, also searches across prompt version content."),
|
|
460
|
+
semantic_search: z.boolean().optional().describe("Enable semantic (vector) search instead of text matching. Requires search_query to be set. Currently supports prompts and folders."),
|
|
461
|
+
semantic_search_top_k: z.number().int().optional().describe("Max results from semantic search (default: 100, range: 1-500)"),
|
|
462
|
+
semantic_search_threshold: z.number().optional().describe("Max distance threshold for semantic search results (range: (0, 2])"),
|
|
463
|
+
tags: z.array(z.string()).optional().describe("Filter entities by tags (AND logic — all must match). Applies to prompts, workflows, datasets, evaluations."),
|
|
438
464
|
flatten: z.boolean().optional().describe("Flatten nested folder hierarchy (default: false)"),
|
|
439
465
|
include_metadata: z.boolean().optional().describe("Include entity metadata like latest_version_number (default: false)"),
|
|
440
466
|
workspace_id: z.number().int().optional().describe("Workspace ID"),
|
|
@@ -480,6 +506,58 @@ export const ResolveFolderIdArgsSchema = z.object({
|
|
|
480
506
|
});
|
|
481
507
|
|
|
482
508
|
|
|
509
|
+
// ── Search Request Logs (POST /api/public/v2/requests/search) ────────────
|
|
510
|
+
// NOTE: The StructuredFilter and StructuredFilterGroup schemas use loose types
|
|
511
|
+
// (z.unknown()) for value/filters because the backend validates operator-field
|
|
512
|
+
// compatibility at runtime. This is tracked as a known exception in scripts/diff-endpoints.ts.
|
|
513
|
+
|
|
514
|
+
const StructuredFilterSchema = z.object({
|
|
515
|
+
field: z.enum([
|
|
516
|
+
"pl_id", "prompt_id", "engine", "provider_type", "input_text", "output_text",
|
|
517
|
+
"prompt_version_number", "input_tokens", "output_tokens", "cost", "latency_ms",
|
|
518
|
+
"request_start_time", "request_end_time", "status",
|
|
519
|
+
"is_json", "is_tool_call", "is_plain_text",
|
|
520
|
+
"tags", "metadata_keys", "metadata", "tool_names",
|
|
521
|
+
"output", "output_keys", "input_variables", "input_variable_keys",
|
|
522
|
+
]).describe("Request log field to filter on"),
|
|
523
|
+
operator: z.enum([
|
|
524
|
+
"is", "is_not", "in", "not_in",
|
|
525
|
+
"contains", "not_contains", "starts_with", "ends_with",
|
|
526
|
+
"eq", "neq", "gt", "gte", "lt", "lte", "between",
|
|
527
|
+
"before", "after",
|
|
528
|
+
"is_true", "is_false", "is_empty", "is_not_empty",
|
|
529
|
+
"is_null", "is_not_null",
|
|
530
|
+
"key_equals", "key_not_equals", "key_contains",
|
|
531
|
+
]).describe("Filter operator (availability depends on field type)"),
|
|
532
|
+
value: z.unknown().optional().describe("Filter value (type depends on operator)"),
|
|
533
|
+
nested_key: z.string().optional().describe("Key name for nested field operators (metadata, output, input_variables)"),
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
const StructuredFilterGroupSchema: z.ZodType = z.object({
|
|
537
|
+
logic: z.enum(["AND", "OR"]).optional().describe("Logical operator (default: AND)"),
|
|
538
|
+
filters: z.array(z.union([StructuredFilterSchema, z.lazy(() => StructuredFilterGroupSchema)])).describe("Filters or nested filter groups"),
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
export const SearchRequestLogsArgsSchema = z.object({
|
|
542
|
+
filters: z.array(StructuredFilterSchema).optional().describe("Structured filters (combined with AND logic)"),
|
|
543
|
+
filter_group: StructuredFilterGroupSchema.optional().describe("Filter group with AND/OR logic, supports nesting"),
|
|
544
|
+
q: z.string().optional().describe("Free-text search across prompt input and LLM output"),
|
|
545
|
+
page: z.number().int().optional().describe("Page number (default: 1)"),
|
|
546
|
+
per_page: z.number().int().optional().describe("Items per page (max: 25)"),
|
|
547
|
+
sort_by: z.enum(["request_start_time", "input_tokens", "output_tokens", "cost", "latency_ms", "status"]).optional().describe("Sort field"),
|
|
548
|
+
sort_order: z.enum(["asc", "desc"]).optional().describe("Sort direction (must be provided with sort_by)"),
|
|
549
|
+
include_prompt_name: z.boolean().optional().describe("Include prompt template name in results"),
|
|
550
|
+
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// ── Get Request (GET /api/public/v2/requests/{request_id}) ───────────────
|
|
554
|
+
|
|
555
|
+
export const GetRequestArgsSchema = z.object({
|
|
556
|
+
request_id: z.number().int().describe("Request ID to retrieve"),
|
|
557
|
+
api_key: z.string().optional().describe("PromptLayer API key (optional, defaults to PROMPTLAYER_API_KEY env var)"),
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
|
|
483
561
|
export type GetPromptTemplateParams = Omit<
|
|
484
562
|
z.infer<typeof GetPromptTemplateArgsSchema>,
|
|
485
563
|
"prompt_name" | "api_key"
|
|
@@ -563,6 +641,40 @@ export const TOOL_DEFINITIONS = {
|
|
|
563
641
|
annotations: { readOnlyHint: true },
|
|
564
642
|
},
|
|
565
643
|
|
|
644
|
+
// ── Request Logs ──────────────────────────────────────────────────
|
|
645
|
+
"search-request-logs": {
|
|
646
|
+
name: "search-request-logs",
|
|
647
|
+
description:
|
|
648
|
+
"Search and filter request logs using structured filters, free-text search, and sorting. " +
|
|
649
|
+
"Rate limited to 10 req/min, max 25 results/page.\n\n" +
|
|
650
|
+
"FILTER SYNTAX: Each filter is {field, operator, value, nested_key?}.\n" +
|
|
651
|
+
"Operators by field type:\n" +
|
|
652
|
+
" - String fields (engine, provider_type): is, is_not, in, not_in\n" +
|
|
653
|
+
" - Text fields (input_text, output_text): contains, not_contains, starts_with, ends_with\n" +
|
|
654
|
+
" - Numeric fields (cost, latency_ms, input_tokens, output_tokens): eq, neq, gt, gte, lt, lte, between (value=[min,max]), is_null, is_not_null\n" +
|
|
655
|
+
" - Datetime fields (request_start_time, request_end_time): is, before, after, between (value=[start,end] as ISO 8601)\n" +
|
|
656
|
+
" - Boolean fields (is_json, is_tool_call, is_plain_text): is_true, is_false\n" +
|
|
657
|
+
" - Array fields (tags, metadata_keys, tool_names, output_keys, input_variable_keys): contains, not_contains, in, not_in, is_empty, is_not_empty\n" +
|
|
658
|
+
" - Nested fields (metadata, output, input_variables): key_equals, key_not_equals, key_contains, in, not_in, is_empty, is_not_empty — requires nested_key\n\n" +
|
|
659
|
+
"EXAMPLES:\n" +
|
|
660
|
+
' Find GPT-4o requests: {filters: [{field:"engine", operator:"is", value:"gpt-4o"}]}\n' +
|
|
661
|
+
' Expensive requests: {filters: [{field:"cost", operator:"gte", value:0.10}]}\n' +
|
|
662
|
+
' By metadata: {filters: [{field:"metadata", operator:"key_equals", value:"customer_123", nested_key:"user_id"}]}\n' +
|
|
663
|
+
' Free-text search: {q: "refund policy"}\n' +
|
|
664
|
+
' Complex AND/OR: {filter_group: {logic:"OR", filters: [{field:"tags", operator:"contains", value:"prod"}, {logic:"AND", filters: [...]}]}}',
|
|
665
|
+
inputSchema: SearchRequestLogsArgsSchema,
|
|
666
|
+
annotations: { readOnlyHint: true },
|
|
667
|
+
},
|
|
668
|
+
"get-request": {
|
|
669
|
+
name: "get-request",
|
|
670
|
+
description:
|
|
671
|
+
"Retrieve a single request's full payload by ID, returned as a prompt blueprint. " +
|
|
672
|
+
"Includes the prompt template content, model configuration, provider, token counts, " +
|
|
673
|
+
"cost, and timing data. Useful for debugging, replaying requests, or extracting data for evaluations.",
|
|
674
|
+
inputSchema: GetRequestArgsSchema,
|
|
675
|
+
annotations: { readOnlyHint: true },
|
|
676
|
+
},
|
|
677
|
+
|
|
566
678
|
// ── Tracking ────────────────────────────────────────────────────────
|
|
567
679
|
"log-request": {
|
|
568
680
|
name: "log-request",
|
|
@@ -605,6 +717,12 @@ export const TOOL_DEFINITIONS = {
|
|
|
605
717
|
inputSchema: CreateDatasetVersionFromFilterParamsArgsSchema,
|
|
606
718
|
annotations: { readOnlyHint: false },
|
|
607
719
|
},
|
|
720
|
+
"get-dataset-rows": {
|
|
721
|
+
name: "get-dataset-rows",
|
|
722
|
+
description: "Get paginated rows from a dataset. Each row is an array of cells with {type: 'dataset', value: ...}. Supports search via the q parameter.",
|
|
723
|
+
inputSchema: GetDatasetRowsArgsSchema,
|
|
724
|
+
annotations: { readOnlyHint: true },
|
|
725
|
+
},
|
|
608
726
|
|
|
609
727
|
// ── Evaluations ─────────────────────────────────────────────────────
|
|
610
728
|
"list-evaluations": {
|
|
@@ -613,6 +731,12 @@ export const TOOL_DEFINITIONS = {
|
|
|
613
731
|
inputSchema: ListEvaluationsArgsSchema,
|
|
614
732
|
annotations: { readOnlyHint: true },
|
|
615
733
|
},
|
|
734
|
+
"get-evaluation-rows": {
|
|
735
|
+
name: "get-evaluation-rows",
|
|
736
|
+
description: "Get paginated evaluation results with dataset inputs and eval outcomes. Each row has dataset cells ({type: 'dataset', value: ...}) followed by eval cells ({type: 'eval', status: 'PASSED'|'FAILED', value: ...}).",
|
|
737
|
+
inputSchema: GetEvaluationRowsArgsSchema,
|
|
738
|
+
annotations: { readOnlyHint: true },
|
|
739
|
+
},
|
|
616
740
|
"create-report": {
|
|
617
741
|
name: "create-report",
|
|
618
742
|
description: "Create an evaluation pipeline (called 'report' in the API) linked to a dataset group. The recommended approach is to add LLM assertion columns that use a language model to score each row. For all available column types, search the PromptLayer docs or visit https://docs.promptlayer.com/features/evaluations/column-types.",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(npm run build:*)",
|
|
5
|
-
"Bash(npm install:*)",
|
|
6
|
-
"Bash(npm run sync:check:*)",
|
|
7
|
-
"WebSearch",
|
|
8
|
-
"WebFetch(domain:blog.modelcontextprotocol.io)",
|
|
9
|
-
"Bash(npx tsx:*)",
|
|
10
|
-
"WebFetch(domain:docs.promptlayer.com)",
|
|
11
|
-
"Bash(git add:*)",
|
|
12
|
-
"Bash(git commit:*)",
|
|
13
|
-
"Bash(git push:*)"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
16
|
-
}
|