@prism-llm-labs/mcp-sdk 0.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/dist/index.d.mts +310 -0
- package/dist/index.d.ts +310 -0
- package/dist/index.js +448 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +414 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +31 -0
- package/src/budget.ts +59 -0
- package/src/index.ts +15 -0
- package/src/pricing.ts +49 -0
- package/src/prism-mcp.ts +352 -0
- package/src/session.ts +132 -0
- package/src/tracker.ts +56 -0
- package/src/types.ts +90 -0
- package/tsconfig.json +15 -0
- package/tsup.config.ts +9 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
type McpPrimitiveType = "tool" | "resource" | "prompt" | "sampling";
|
|
2
|
+
interface McpEvent {
|
|
3
|
+
event_id: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
session_id: string;
|
|
6
|
+
org_id: string;
|
|
7
|
+
project_id: string;
|
|
8
|
+
team_id: string;
|
|
9
|
+
user_id: string;
|
|
10
|
+
environment: string;
|
|
11
|
+
mcp_server_name: string;
|
|
12
|
+
/** tool name, resource URI, prompt name, or model hint for sampling */
|
|
13
|
+
tool_name: string;
|
|
14
|
+
downstream_resource: string;
|
|
15
|
+
execution_latency_ms: number;
|
|
16
|
+
tool_cost_usd: number;
|
|
17
|
+
status: "ok" | "error" | "timeout";
|
|
18
|
+
error_message: string;
|
|
19
|
+
llm_request_id: string;
|
|
20
|
+
primitive_type: McpPrimitiveType;
|
|
21
|
+
/**
|
|
22
|
+
* Whether tool_cost_usd is an estimate from the built-in catalog ("estimated")
|
|
23
|
+
* or a real figure provided by the tool via ctx.reportActualCost() ("actual").
|
|
24
|
+
*/
|
|
25
|
+
cost_status: "estimated" | "actual";
|
|
26
|
+
tags: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
interface PrismMcpOptions {
|
|
29
|
+
/** Prism API key — or set PRISM_API_KEY env var */
|
|
30
|
+
prismKey?: string;
|
|
31
|
+
/** Project ID for cost attribution */
|
|
32
|
+
project?: string;
|
|
33
|
+
/** Team attribution tag */
|
|
34
|
+
team?: string;
|
|
35
|
+
/** "production" | "staging" | "development" */
|
|
36
|
+
environment?: string;
|
|
37
|
+
/** Explicit session ID. Auto-generated UUID if omitted. */
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
/** MCP server name shown in the dashboard */
|
|
40
|
+
serverName?: string;
|
|
41
|
+
/** Override ingest URL (for testing) */
|
|
42
|
+
ingestUrl?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Session budget in USD. Tool/resource/prompt calls are blocked when the
|
|
45
|
+
* combined session cost exceeds this value.
|
|
46
|
+
*/
|
|
47
|
+
sessionBudgetUsd?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Maximum MCP primitive calls per session. Blocks further calls when exceeded.
|
|
50
|
+
* Loop detection guard — default unlimited.
|
|
51
|
+
*/
|
|
52
|
+
maxToolCallsPerSession?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Log call arguments into tags['tool_input'] (truncated to 1000 chars).
|
|
55
|
+
* Opt-in only — disabled by default for privacy.
|
|
56
|
+
*/
|
|
57
|
+
captureInputs?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Log call results into tags['tool_output'] (truncated to 1000 chars).
|
|
60
|
+
* Opt-in only — disabled by default for privacy.
|
|
61
|
+
*/
|
|
62
|
+
captureOutputs?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Keys to redact from captured inputs/outputs.
|
|
65
|
+
* Default: ['password', 'token', 'key', 'secret', 'api_key', 'authorization']
|
|
66
|
+
*/
|
|
67
|
+
redactKeys?: string[];
|
|
68
|
+
}
|
|
69
|
+
declare class PrismSessionBudgetExceededError extends Error {
|
|
70
|
+
constructor(sessionId: string, budgetUsd: number);
|
|
71
|
+
}
|
|
72
|
+
declare class PrismToolCallLimitError extends Error {
|
|
73
|
+
constructor(sessionId: string, limit: number);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* PrismMCP — instruments any MCP Server with full observability:
|
|
78
|
+
* - tools/call → wrapToolCall() / patchHandler()
|
|
79
|
+
* - resources/read → wrapResourceRead() / patchResourceHandler()
|
|
80
|
+
* - prompts/get → wrapPromptGet() / patchPromptHandler()
|
|
81
|
+
* - sampling/createMessage → wrapSamplingHandler()
|
|
82
|
+
*
|
|
83
|
+
* Features:
|
|
84
|
+
* - Session budget circuit breaker (throws before execution if over budget)
|
|
85
|
+
* - Tool loop detection (throws if max_tool_calls_per_session exceeded)
|
|
86
|
+
* - Opt-in I/O capture (captureInputs / captureOutputs)
|
|
87
|
+
* - Streaming tool latency: correctly measures time-to-stream-end, not first-chunk
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
declare class WrapContext {
|
|
91
|
+
/** @internal - read by _wrap() after fn() completes */
|
|
92
|
+
_actualCostUsd: number | null;
|
|
93
|
+
/**
|
|
94
|
+
* Override the catalog-estimated tool cost with the real billing figure.
|
|
95
|
+
* Call this inside your tool handler when you have access to actual cost data
|
|
96
|
+
* (e.g. from AWS SDK response metadata, a billing header, or a usage API).
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* await prismMcp.wrapToolCall("invoke_lambda", async (ctx) => {
|
|
100
|
+
* const res = await lambda.invoke({ FunctionName: "fn", Payload: payload });
|
|
101
|
+
* const billedMs = res.$metadata.httpHeaders?.["x-amz-billed-duration-ms"] ?? "0";
|
|
102
|
+
* ctx.reportActualCost(parseInt(billedMs) * 0.000016667 / 1000);
|
|
103
|
+
* return res;
|
|
104
|
+
* });
|
|
105
|
+
*/
|
|
106
|
+
reportActualCost(usd: number): void;
|
|
107
|
+
}
|
|
108
|
+
declare class PrismMCP {
|
|
109
|
+
private readonly tracker;
|
|
110
|
+
private readonly budget;
|
|
111
|
+
private readonly redactKeys;
|
|
112
|
+
private readonly opts;
|
|
113
|
+
constructor(options?: PrismMcpOptions);
|
|
114
|
+
/**
|
|
115
|
+
* Internal wrapper used by all four MCP primitives.
|
|
116
|
+
* Handles: budget check, I/O capture, streaming proxy, fire-and-forget telemetry,
|
|
117
|
+
* and actual cost self-reporting via ctx.reportActualCost().
|
|
118
|
+
*/
|
|
119
|
+
private _wrap;
|
|
120
|
+
/**
|
|
121
|
+
* Wrap a tools/call execution with Prism instrumentation.
|
|
122
|
+
*/
|
|
123
|
+
wrapToolCall<T>(toolName: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
124
|
+
llmRequestId?: string;
|
|
125
|
+
tags?: Record<string, string>;
|
|
126
|
+
downstreamResource?: string;
|
|
127
|
+
/** Raw tool arguments — captured if captureInputs: true */
|
|
128
|
+
inputs?: Record<string, unknown>;
|
|
129
|
+
}): Promise<T>;
|
|
130
|
+
/**
|
|
131
|
+
* Drop-in patch for MCP SDK's CallToolRequestSchema handler.
|
|
132
|
+
* Automatically passes req.params.arguments as inputs when captureInputs: true.
|
|
133
|
+
* ctx is available for reportActualCost() inside the handler.
|
|
134
|
+
*/
|
|
135
|
+
patchHandler<TReq extends {
|
|
136
|
+
params: {
|
|
137
|
+
name: string;
|
|
138
|
+
arguments?: Record<string, unknown>;
|
|
139
|
+
};
|
|
140
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
141
|
+
/**
|
|
142
|
+
* Wrap a resources/read execution with Prism instrumentation.
|
|
143
|
+
*
|
|
144
|
+
* @param resourceUri - The resource URI being read (e.g. "file:///path/to/file")
|
|
145
|
+
*/
|
|
146
|
+
wrapResourceRead<T>(resourceUri: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
147
|
+
llmRequestId?: string;
|
|
148
|
+
tags?: Record<string, string>;
|
|
149
|
+
}): Promise<T>;
|
|
150
|
+
patchResourceHandler<TReq extends {
|
|
151
|
+
params: {
|
|
152
|
+
uri: string;
|
|
153
|
+
};
|
|
154
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
155
|
+
wrapPromptGet<T>(promptName: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
156
|
+
llmRequestId?: string;
|
|
157
|
+
tags?: Record<string, string>;
|
|
158
|
+
}): Promise<T>;
|
|
159
|
+
patchPromptHandler<TReq extends {
|
|
160
|
+
params: {
|
|
161
|
+
name: string;
|
|
162
|
+
};
|
|
163
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
164
|
+
/**
|
|
165
|
+
* Wrap the MCP client's sampling/createMessage callback so LLM calls
|
|
166
|
+
* requested by the MCP server appear in the session timeline.
|
|
167
|
+
*
|
|
168
|
+
* Usage with @modelcontextprotocol/sdk:
|
|
169
|
+
* client.setRequestHandler(
|
|
170
|
+
* CreateMessageRequestSchema,
|
|
171
|
+
* prismMcp.wrapSamplingHandler(async (req) => {
|
|
172
|
+
* const res = await openai.chat.completions.create({ ... });
|
|
173
|
+
* return { role: "assistant", content: [{ type: "text", text: res.choices[0].message.content }] };
|
|
174
|
+
* })
|
|
175
|
+
* );
|
|
176
|
+
*/
|
|
177
|
+
wrapSamplingHandler<TReq extends {
|
|
178
|
+
params?: {
|
|
179
|
+
modelPreferences?: {
|
|
180
|
+
hints?: Array<{
|
|
181
|
+
name?: string;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
}, TRes>(handler: (req: TReq) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
186
|
+
/** The session_id assigned to this instance (useful for logging). */
|
|
187
|
+
get sessionId(): string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* PrismSession — shared session context for multi-server agent runs.
|
|
192
|
+
*
|
|
193
|
+
* Creates a single session_id that is automatically threaded through:
|
|
194
|
+
* - Multiple PrismMCP server instances (files, database, search, etc.)
|
|
195
|
+
* - LLM SDK clients (OpenAI, Anthropic) via toLLMOptions()
|
|
196
|
+
*
|
|
197
|
+
* This ensures every LLM call and every tool/resource/prompt call in one
|
|
198
|
+
* agent run appears under the same session in /dashboard/sessions/[id].
|
|
199
|
+
*
|
|
200
|
+
* Usage:
|
|
201
|
+
* const session = new PrismSession({
|
|
202
|
+
* prismKey: process.env.PRISM_API_KEY,
|
|
203
|
+
* project: "customer-support",
|
|
204
|
+
* sessionBudgetUsd: 2.00, // hard cap for the whole agent run
|
|
205
|
+
* });
|
|
206
|
+
*
|
|
207
|
+
* // MCP servers — all share session.sessionId automatically
|
|
208
|
+
* const filesMcp = session.createServer({ serverName: "files" });
|
|
209
|
+
* const dbMcp = session.createServer({ serverName: "database" });
|
|
210
|
+
* const searchMcp = session.createServer({ serverName: "search" });
|
|
211
|
+
*
|
|
212
|
+
* // LLM SDK client — same session_id
|
|
213
|
+
* import { OpenAI } from "@prism-llm-labs/sdk";
|
|
214
|
+
* const openai = new OpenAI(session.toLLMOptions());
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
interface PrismSessionOptions {
|
|
218
|
+
/** Prism API key — or set PRISM_API_KEY env var */
|
|
219
|
+
prismKey?: string;
|
|
220
|
+
/** Project ID for cost attribution */
|
|
221
|
+
project?: string;
|
|
222
|
+
/** Team attribution tag */
|
|
223
|
+
team?: string;
|
|
224
|
+
/** "production" | "staging" | "development" */
|
|
225
|
+
environment?: string;
|
|
226
|
+
/**
|
|
227
|
+
* Explicit session ID. Auto-generated UUID if omitted.
|
|
228
|
+
* Pass an explicit ID when the session ID is determined by an external
|
|
229
|
+
* orchestrator (e.g. a LangGraph run ID, a task queue job ID).
|
|
230
|
+
*/
|
|
231
|
+
sessionId?: string;
|
|
232
|
+
/** Hard cost cap across ALL servers in this session */
|
|
233
|
+
sessionBudgetUsd?: number;
|
|
234
|
+
/** Hard tool-call cap across ALL servers in this session */
|
|
235
|
+
maxToolCallsPerSession?: number;
|
|
236
|
+
/** Opt-in I/O capture for all servers created from this session */
|
|
237
|
+
captureInputs?: boolean;
|
|
238
|
+
captureOutputs?: boolean;
|
|
239
|
+
redactKeys?: string[];
|
|
240
|
+
/** Override ingest URL (for testing) */
|
|
241
|
+
ingestUrl?: string;
|
|
242
|
+
}
|
|
243
|
+
/** Options for individual servers within a session */
|
|
244
|
+
type PerServerOptions = Omit<PrismMcpOptions, "prismKey" | "project" | "team" | "environment" | "sessionId" | "sessionBudgetUsd" | "maxToolCallsPerSession" | "captureInputs" | "captureOutputs" | "redactKeys" | "ingestUrl">;
|
|
245
|
+
declare class PrismSession {
|
|
246
|
+
readonly sessionId: string;
|
|
247
|
+
private readonly key;
|
|
248
|
+
private readonly project;
|
|
249
|
+
private readonly team;
|
|
250
|
+
private readonly environment;
|
|
251
|
+
private readonly sharedOpts;
|
|
252
|
+
constructor(options?: PrismSessionOptions);
|
|
253
|
+
/**
|
|
254
|
+
* Create a PrismMCP instance bound to this session.
|
|
255
|
+
* All servers created from the same session share the same session_id.
|
|
256
|
+
*/
|
|
257
|
+
createServer(perServer?: PerServerOptions): PrismMCP;
|
|
258
|
+
/**
|
|
259
|
+
* Returns options to pass to the Prism LLM SDK clients (@prism-llm-labs/sdk)
|
|
260
|
+
* so that LLM completions share this session's session_id.
|
|
261
|
+
*
|
|
262
|
+
* import { OpenAI } from "@prism-llm-labs/sdk";
|
|
263
|
+
* const openai = new OpenAI(session.toLLMOptions());
|
|
264
|
+
*/
|
|
265
|
+
toLLMOptions(): {
|
|
266
|
+
prismKey: string;
|
|
267
|
+
project: string;
|
|
268
|
+
team: string;
|
|
269
|
+
environment: string;
|
|
270
|
+
sessionId: string;
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
declare class McpEventTracker {
|
|
275
|
+
private readonly key;
|
|
276
|
+
private readonly ingestUrl;
|
|
277
|
+
private readonly serverName;
|
|
278
|
+
private readonly orgId;
|
|
279
|
+
constructor(key: string, serverName: string, ingestUrl?: string);
|
|
280
|
+
capture(event: Omit<McpEvent, "event_id" | "org_id" | "mcp_server_name">): Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Session budget circuit breaker.
|
|
285
|
+
* Checks Redis session cost + tool-call counters before each tool invocation.
|
|
286
|
+
* If the UPSTASH env vars are absent, checks are skipped (graceful degradation).
|
|
287
|
+
*/
|
|
288
|
+
declare class SessionBudgetChecker {
|
|
289
|
+
private readonly url;
|
|
290
|
+
private readonly token;
|
|
291
|
+
private readonly orgId;
|
|
292
|
+
constructor(orgId: string);
|
|
293
|
+
/**
|
|
294
|
+
* Throws PrismSessionBudgetExceededError or PrismToolCallLimitError
|
|
295
|
+
* if the session has exceeded its configured limits.
|
|
296
|
+
*/
|
|
297
|
+
checkOrThrow(sessionId: string, sessionBudgetUsd?: number, maxToolCallsPerSession?: number): Promise<void>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Built-in tool cost estimates (USD per call).
|
|
302
|
+
* Mirrors apps/web/lib/pricing/tool-catalog.ts — keep in sync.
|
|
303
|
+
*/
|
|
304
|
+
/**
|
|
305
|
+
* Look up estimated cost for a tool name.
|
|
306
|
+
* Exact match, then prefix match ("pinecone_*"), then 0.
|
|
307
|
+
*/
|
|
308
|
+
declare function lookupToolCost(toolName: string, overrides?: Record<string, number>): number;
|
|
309
|
+
|
|
310
|
+
export { type McpEvent, McpEventTracker, type McpPrimitiveType, type PerServerOptions, PrismMCP, type PrismMcpOptions, PrismSession, PrismSessionBudgetExceededError, type PrismSessionOptions, PrismToolCallLimitError, SessionBudgetChecker, WrapContext, lookupToolCost };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
type McpPrimitiveType = "tool" | "resource" | "prompt" | "sampling";
|
|
2
|
+
interface McpEvent {
|
|
3
|
+
event_id: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
session_id: string;
|
|
6
|
+
org_id: string;
|
|
7
|
+
project_id: string;
|
|
8
|
+
team_id: string;
|
|
9
|
+
user_id: string;
|
|
10
|
+
environment: string;
|
|
11
|
+
mcp_server_name: string;
|
|
12
|
+
/** tool name, resource URI, prompt name, or model hint for sampling */
|
|
13
|
+
tool_name: string;
|
|
14
|
+
downstream_resource: string;
|
|
15
|
+
execution_latency_ms: number;
|
|
16
|
+
tool_cost_usd: number;
|
|
17
|
+
status: "ok" | "error" | "timeout";
|
|
18
|
+
error_message: string;
|
|
19
|
+
llm_request_id: string;
|
|
20
|
+
primitive_type: McpPrimitiveType;
|
|
21
|
+
/**
|
|
22
|
+
* Whether tool_cost_usd is an estimate from the built-in catalog ("estimated")
|
|
23
|
+
* or a real figure provided by the tool via ctx.reportActualCost() ("actual").
|
|
24
|
+
*/
|
|
25
|
+
cost_status: "estimated" | "actual";
|
|
26
|
+
tags: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
interface PrismMcpOptions {
|
|
29
|
+
/** Prism API key — or set PRISM_API_KEY env var */
|
|
30
|
+
prismKey?: string;
|
|
31
|
+
/** Project ID for cost attribution */
|
|
32
|
+
project?: string;
|
|
33
|
+
/** Team attribution tag */
|
|
34
|
+
team?: string;
|
|
35
|
+
/** "production" | "staging" | "development" */
|
|
36
|
+
environment?: string;
|
|
37
|
+
/** Explicit session ID. Auto-generated UUID if omitted. */
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
/** MCP server name shown in the dashboard */
|
|
40
|
+
serverName?: string;
|
|
41
|
+
/** Override ingest URL (for testing) */
|
|
42
|
+
ingestUrl?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Session budget in USD. Tool/resource/prompt calls are blocked when the
|
|
45
|
+
* combined session cost exceeds this value.
|
|
46
|
+
*/
|
|
47
|
+
sessionBudgetUsd?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Maximum MCP primitive calls per session. Blocks further calls when exceeded.
|
|
50
|
+
* Loop detection guard — default unlimited.
|
|
51
|
+
*/
|
|
52
|
+
maxToolCallsPerSession?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Log call arguments into tags['tool_input'] (truncated to 1000 chars).
|
|
55
|
+
* Opt-in only — disabled by default for privacy.
|
|
56
|
+
*/
|
|
57
|
+
captureInputs?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Log call results into tags['tool_output'] (truncated to 1000 chars).
|
|
60
|
+
* Opt-in only — disabled by default for privacy.
|
|
61
|
+
*/
|
|
62
|
+
captureOutputs?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Keys to redact from captured inputs/outputs.
|
|
65
|
+
* Default: ['password', 'token', 'key', 'secret', 'api_key', 'authorization']
|
|
66
|
+
*/
|
|
67
|
+
redactKeys?: string[];
|
|
68
|
+
}
|
|
69
|
+
declare class PrismSessionBudgetExceededError extends Error {
|
|
70
|
+
constructor(sessionId: string, budgetUsd: number);
|
|
71
|
+
}
|
|
72
|
+
declare class PrismToolCallLimitError extends Error {
|
|
73
|
+
constructor(sessionId: string, limit: number);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* PrismMCP — instruments any MCP Server with full observability:
|
|
78
|
+
* - tools/call → wrapToolCall() / patchHandler()
|
|
79
|
+
* - resources/read → wrapResourceRead() / patchResourceHandler()
|
|
80
|
+
* - prompts/get → wrapPromptGet() / patchPromptHandler()
|
|
81
|
+
* - sampling/createMessage → wrapSamplingHandler()
|
|
82
|
+
*
|
|
83
|
+
* Features:
|
|
84
|
+
* - Session budget circuit breaker (throws before execution if over budget)
|
|
85
|
+
* - Tool loop detection (throws if max_tool_calls_per_session exceeded)
|
|
86
|
+
* - Opt-in I/O capture (captureInputs / captureOutputs)
|
|
87
|
+
* - Streaming tool latency: correctly measures time-to-stream-end, not first-chunk
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
declare class WrapContext {
|
|
91
|
+
/** @internal - read by _wrap() after fn() completes */
|
|
92
|
+
_actualCostUsd: number | null;
|
|
93
|
+
/**
|
|
94
|
+
* Override the catalog-estimated tool cost with the real billing figure.
|
|
95
|
+
* Call this inside your tool handler when you have access to actual cost data
|
|
96
|
+
* (e.g. from AWS SDK response metadata, a billing header, or a usage API).
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* await prismMcp.wrapToolCall("invoke_lambda", async (ctx) => {
|
|
100
|
+
* const res = await lambda.invoke({ FunctionName: "fn", Payload: payload });
|
|
101
|
+
* const billedMs = res.$metadata.httpHeaders?.["x-amz-billed-duration-ms"] ?? "0";
|
|
102
|
+
* ctx.reportActualCost(parseInt(billedMs) * 0.000016667 / 1000);
|
|
103
|
+
* return res;
|
|
104
|
+
* });
|
|
105
|
+
*/
|
|
106
|
+
reportActualCost(usd: number): void;
|
|
107
|
+
}
|
|
108
|
+
declare class PrismMCP {
|
|
109
|
+
private readonly tracker;
|
|
110
|
+
private readonly budget;
|
|
111
|
+
private readonly redactKeys;
|
|
112
|
+
private readonly opts;
|
|
113
|
+
constructor(options?: PrismMcpOptions);
|
|
114
|
+
/**
|
|
115
|
+
* Internal wrapper used by all four MCP primitives.
|
|
116
|
+
* Handles: budget check, I/O capture, streaming proxy, fire-and-forget telemetry,
|
|
117
|
+
* and actual cost self-reporting via ctx.reportActualCost().
|
|
118
|
+
*/
|
|
119
|
+
private _wrap;
|
|
120
|
+
/**
|
|
121
|
+
* Wrap a tools/call execution with Prism instrumentation.
|
|
122
|
+
*/
|
|
123
|
+
wrapToolCall<T>(toolName: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
124
|
+
llmRequestId?: string;
|
|
125
|
+
tags?: Record<string, string>;
|
|
126
|
+
downstreamResource?: string;
|
|
127
|
+
/** Raw tool arguments — captured if captureInputs: true */
|
|
128
|
+
inputs?: Record<string, unknown>;
|
|
129
|
+
}): Promise<T>;
|
|
130
|
+
/**
|
|
131
|
+
* Drop-in patch for MCP SDK's CallToolRequestSchema handler.
|
|
132
|
+
* Automatically passes req.params.arguments as inputs when captureInputs: true.
|
|
133
|
+
* ctx is available for reportActualCost() inside the handler.
|
|
134
|
+
*/
|
|
135
|
+
patchHandler<TReq extends {
|
|
136
|
+
params: {
|
|
137
|
+
name: string;
|
|
138
|
+
arguments?: Record<string, unknown>;
|
|
139
|
+
};
|
|
140
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
141
|
+
/**
|
|
142
|
+
* Wrap a resources/read execution with Prism instrumentation.
|
|
143
|
+
*
|
|
144
|
+
* @param resourceUri - The resource URI being read (e.g. "file:///path/to/file")
|
|
145
|
+
*/
|
|
146
|
+
wrapResourceRead<T>(resourceUri: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
147
|
+
llmRequestId?: string;
|
|
148
|
+
tags?: Record<string, string>;
|
|
149
|
+
}): Promise<T>;
|
|
150
|
+
patchResourceHandler<TReq extends {
|
|
151
|
+
params: {
|
|
152
|
+
uri: string;
|
|
153
|
+
};
|
|
154
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
155
|
+
wrapPromptGet<T>(promptName: string, fn: (ctx: WrapContext) => Promise<T>, extra?: {
|
|
156
|
+
llmRequestId?: string;
|
|
157
|
+
tags?: Record<string, string>;
|
|
158
|
+
}): Promise<T>;
|
|
159
|
+
patchPromptHandler<TReq extends {
|
|
160
|
+
params: {
|
|
161
|
+
name: string;
|
|
162
|
+
};
|
|
163
|
+
}, TRes>(handler: (req: TReq, ctx: WrapContext) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
164
|
+
/**
|
|
165
|
+
* Wrap the MCP client's sampling/createMessage callback so LLM calls
|
|
166
|
+
* requested by the MCP server appear in the session timeline.
|
|
167
|
+
*
|
|
168
|
+
* Usage with @modelcontextprotocol/sdk:
|
|
169
|
+
* client.setRequestHandler(
|
|
170
|
+
* CreateMessageRequestSchema,
|
|
171
|
+
* prismMcp.wrapSamplingHandler(async (req) => {
|
|
172
|
+
* const res = await openai.chat.completions.create({ ... });
|
|
173
|
+
* return { role: "assistant", content: [{ type: "text", text: res.choices[0].message.content }] };
|
|
174
|
+
* })
|
|
175
|
+
* );
|
|
176
|
+
*/
|
|
177
|
+
wrapSamplingHandler<TReq extends {
|
|
178
|
+
params?: {
|
|
179
|
+
modelPreferences?: {
|
|
180
|
+
hints?: Array<{
|
|
181
|
+
name?: string;
|
|
182
|
+
}>;
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
}, TRes>(handler: (req: TReq) => Promise<TRes>): (req: TReq) => Promise<TRes>;
|
|
186
|
+
/** The session_id assigned to this instance (useful for logging). */
|
|
187
|
+
get sessionId(): string;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* PrismSession — shared session context for multi-server agent runs.
|
|
192
|
+
*
|
|
193
|
+
* Creates a single session_id that is automatically threaded through:
|
|
194
|
+
* - Multiple PrismMCP server instances (files, database, search, etc.)
|
|
195
|
+
* - LLM SDK clients (OpenAI, Anthropic) via toLLMOptions()
|
|
196
|
+
*
|
|
197
|
+
* This ensures every LLM call and every tool/resource/prompt call in one
|
|
198
|
+
* agent run appears under the same session in /dashboard/sessions/[id].
|
|
199
|
+
*
|
|
200
|
+
* Usage:
|
|
201
|
+
* const session = new PrismSession({
|
|
202
|
+
* prismKey: process.env.PRISM_API_KEY,
|
|
203
|
+
* project: "customer-support",
|
|
204
|
+
* sessionBudgetUsd: 2.00, // hard cap for the whole agent run
|
|
205
|
+
* });
|
|
206
|
+
*
|
|
207
|
+
* // MCP servers — all share session.sessionId automatically
|
|
208
|
+
* const filesMcp = session.createServer({ serverName: "files" });
|
|
209
|
+
* const dbMcp = session.createServer({ serverName: "database" });
|
|
210
|
+
* const searchMcp = session.createServer({ serverName: "search" });
|
|
211
|
+
*
|
|
212
|
+
* // LLM SDK client — same session_id
|
|
213
|
+
* import { OpenAI } from "@prism-llm-labs/sdk";
|
|
214
|
+
* const openai = new OpenAI(session.toLLMOptions());
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
interface PrismSessionOptions {
|
|
218
|
+
/** Prism API key — or set PRISM_API_KEY env var */
|
|
219
|
+
prismKey?: string;
|
|
220
|
+
/** Project ID for cost attribution */
|
|
221
|
+
project?: string;
|
|
222
|
+
/** Team attribution tag */
|
|
223
|
+
team?: string;
|
|
224
|
+
/** "production" | "staging" | "development" */
|
|
225
|
+
environment?: string;
|
|
226
|
+
/**
|
|
227
|
+
* Explicit session ID. Auto-generated UUID if omitted.
|
|
228
|
+
* Pass an explicit ID when the session ID is determined by an external
|
|
229
|
+
* orchestrator (e.g. a LangGraph run ID, a task queue job ID).
|
|
230
|
+
*/
|
|
231
|
+
sessionId?: string;
|
|
232
|
+
/** Hard cost cap across ALL servers in this session */
|
|
233
|
+
sessionBudgetUsd?: number;
|
|
234
|
+
/** Hard tool-call cap across ALL servers in this session */
|
|
235
|
+
maxToolCallsPerSession?: number;
|
|
236
|
+
/** Opt-in I/O capture for all servers created from this session */
|
|
237
|
+
captureInputs?: boolean;
|
|
238
|
+
captureOutputs?: boolean;
|
|
239
|
+
redactKeys?: string[];
|
|
240
|
+
/** Override ingest URL (for testing) */
|
|
241
|
+
ingestUrl?: string;
|
|
242
|
+
}
|
|
243
|
+
/** Options for individual servers within a session */
|
|
244
|
+
type PerServerOptions = Omit<PrismMcpOptions, "prismKey" | "project" | "team" | "environment" | "sessionId" | "sessionBudgetUsd" | "maxToolCallsPerSession" | "captureInputs" | "captureOutputs" | "redactKeys" | "ingestUrl">;
|
|
245
|
+
declare class PrismSession {
|
|
246
|
+
readonly sessionId: string;
|
|
247
|
+
private readonly key;
|
|
248
|
+
private readonly project;
|
|
249
|
+
private readonly team;
|
|
250
|
+
private readonly environment;
|
|
251
|
+
private readonly sharedOpts;
|
|
252
|
+
constructor(options?: PrismSessionOptions);
|
|
253
|
+
/**
|
|
254
|
+
* Create a PrismMCP instance bound to this session.
|
|
255
|
+
* All servers created from the same session share the same session_id.
|
|
256
|
+
*/
|
|
257
|
+
createServer(perServer?: PerServerOptions): PrismMCP;
|
|
258
|
+
/**
|
|
259
|
+
* Returns options to pass to the Prism LLM SDK clients (@prism-llm-labs/sdk)
|
|
260
|
+
* so that LLM completions share this session's session_id.
|
|
261
|
+
*
|
|
262
|
+
* import { OpenAI } from "@prism-llm-labs/sdk";
|
|
263
|
+
* const openai = new OpenAI(session.toLLMOptions());
|
|
264
|
+
*/
|
|
265
|
+
toLLMOptions(): {
|
|
266
|
+
prismKey: string;
|
|
267
|
+
project: string;
|
|
268
|
+
team: string;
|
|
269
|
+
environment: string;
|
|
270
|
+
sessionId: string;
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
declare class McpEventTracker {
|
|
275
|
+
private readonly key;
|
|
276
|
+
private readonly ingestUrl;
|
|
277
|
+
private readonly serverName;
|
|
278
|
+
private readonly orgId;
|
|
279
|
+
constructor(key: string, serverName: string, ingestUrl?: string);
|
|
280
|
+
capture(event: Omit<McpEvent, "event_id" | "org_id" | "mcp_server_name">): Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Session budget circuit breaker.
|
|
285
|
+
* Checks Redis session cost + tool-call counters before each tool invocation.
|
|
286
|
+
* If the UPSTASH env vars are absent, checks are skipped (graceful degradation).
|
|
287
|
+
*/
|
|
288
|
+
declare class SessionBudgetChecker {
|
|
289
|
+
private readonly url;
|
|
290
|
+
private readonly token;
|
|
291
|
+
private readonly orgId;
|
|
292
|
+
constructor(orgId: string);
|
|
293
|
+
/**
|
|
294
|
+
* Throws PrismSessionBudgetExceededError or PrismToolCallLimitError
|
|
295
|
+
* if the session has exceeded its configured limits.
|
|
296
|
+
*/
|
|
297
|
+
checkOrThrow(sessionId: string, sessionBudgetUsd?: number, maxToolCallsPerSession?: number): Promise<void>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Built-in tool cost estimates (USD per call).
|
|
302
|
+
* Mirrors apps/web/lib/pricing/tool-catalog.ts — keep in sync.
|
|
303
|
+
*/
|
|
304
|
+
/**
|
|
305
|
+
* Look up estimated cost for a tool name.
|
|
306
|
+
* Exact match, then prefix match ("pinecone_*"), then 0.
|
|
307
|
+
*/
|
|
308
|
+
declare function lookupToolCost(toolName: string, overrides?: Record<string, number>): number;
|
|
309
|
+
|
|
310
|
+
export { type McpEvent, McpEventTracker, type McpPrimitiveType, type PerServerOptions, PrismMCP, type PrismMcpOptions, PrismSession, PrismSessionBudgetExceededError, type PrismSessionOptions, PrismToolCallLimitError, SessionBudgetChecker, WrapContext, lookupToolCost };
|