@pitcher/cli-scripts 3.1.1 → 3.3.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 +39 -2
- package/dist/index.js +2 -0
- package/dist/src/commands/analytics.d.ts +77 -0
- package/dist/src/commands/analytics.js +309 -1
- package/dist/src/commands/dsrLanding.js +1 -1
- package/dist/src/commands/roleplay.d.ts +236 -0
- package/dist/src/commands/roleplay.js +702 -0
- package/dist/src/core/dsr-engagement.d.ts +152 -0
- package/dist/src/core/dsr-engagement.js +301 -0
- package/dist/src/core/dsr-visits.d.ts +50 -0
- package/dist/src/core/dsr-visits.js +191 -0
- package/dist/src/core/envelope.d.ts +1 -0
- package/dist/src/core/envelope.js +6 -1
- package/dist/src/core/errors.d.ts +3 -1
- package/dist/src/core/errors.js +2 -2
- package/dist/src/core/http.js +7 -5
- package/dist/src/core/ld.js +2 -1
- package/dist/src/core/me.d.ts +3 -0
- package/dist/src/core/me.js +10 -0
- package/dist/src/core/roleplay-stats.d.ts +120 -0
- package/dist/src/core/roleplay-stats.js +171 -0
- package/dist/src/mcp/apps/confirm/define.d.ts +66 -0
- package/dist/src/mcp/apps/confirm/define.js +208 -0
- package/dist/src/mcp/apps/confirm/hash.d.ts +4 -0
- package/dist/src/mcp/apps/confirm/hash.js +23 -0
- package/dist/src/mcp/apps/confirm/types.d.ts +96 -0
- package/dist/src/mcp/apps/confirm/types.js +15 -0
- package/dist/src/mcp/apps/contentPicker.d.ts +104 -0
- package/dist/src/mcp/apps/contentPicker.js +268 -0
- package/dist/src/mcp/apps/dsrEngagement.d.ts +194 -0
- package/dist/src/mcp/apps/dsrEngagement.js +737 -0
- package/dist/src/mcp/apps/registry.d.ts +14 -0
- package/dist/src/mcp/apps/registry.js +25 -0
- package/dist/src/mcp/apps/roleplayManager.d.ts +228 -0
- package/dist/src/mcp/apps/roleplayManager.js +606 -0
- package/dist/src/mcp/apps/roleplayReps.d.ts +79 -0
- package/dist/src/mcp/apps/roleplayReps.js +328 -0
- package/dist/src/mcp/apps/roleplayWrites.d.ts +6 -0
- package/dist/src/mcp/apps/roleplayWrites.js +509 -0
- package/dist/src/mcp/apps/text.d.ts +41 -0
- package/dist/src/mcp/apps/text.js +58 -0
- package/dist/src/mcp/apps/types.d.ts +23 -0
- package/dist/src/mcp/apps/types.js +12 -0
- package/dist/src/mcp/apps/writeConfirm.d.ts +3 -0
- package/dist/src/mcp/apps/writeConfirm.js +8 -0
- package/dist/src/mcp/groups.d.ts +42 -0
- package/dist/src/mcp/groups.js +89 -0
- package/dist/src/mcp/hosted.d.ts +36 -0
- package/dist/src/mcp/hosted.js +47 -1
- package/dist/src/mcp/index.d.ts +9 -3
- package/dist/src/mcp/index.js +9 -2
- package/dist/src/mcp/prompts/registry.js +8 -7
- package/dist/src/mcp/prompts/types.d.ts +3 -0
- package/dist/src/mcp/registry.d.ts +4 -0
- package/dist/src/mcp/registry.js +30 -23
- package/dist/src/mcp/renderPolicy.d.ts +2 -1
- package/dist/src/mcp/renderPolicy.js +10 -3
- package/dist/src/mcp/resources/analyticsCatalog.d.ts +1 -1
- package/dist/src/mcp/resources/analyticsCatalog.js +23 -5
- package/dist/src/mcp/resources/registry.js +2 -2
- package/dist/src/mcp/resources/types.d.ts +3 -0
- package/dist/src/mcp/result.d.ts +2 -1
- package/dist/src/mcp/result.js +8 -6
- package/dist/src/mcp/server.d.ts +62 -0
- package/dist/src/mcp/server.js +161 -10
- package/dist/src/mcp/tools/analytics.js +88 -3
- package/dist/src/mcp/tools/roleplay.d.ts +2 -0
- package/dist/src/mcp/tools/roleplay.js +135 -0
- package/dist/src/mcp/tools/types.d.ts +35 -0
- package/package.json +7 -1
package/dist/src/mcp/server.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ import type { Credential } from '../core/credential.js';
|
|
|
3
3
|
import type { ToolDescriptor } from './tools/types.js';
|
|
4
4
|
import type { PromptDescriptor } from './prompts/types.js';
|
|
5
5
|
import type { ResourceDescriptor } from './resources/types.js';
|
|
6
|
+
import { type AppViewDescriptor } from './apps/types.js';
|
|
7
|
+
import type { HostClient, ProposalSigner } from './apps/confirm/types.js';
|
|
8
|
+
import { type ToolAccess, type ToolGroup, type ToolPolicy } from './groups.js';
|
|
9
|
+
/** The package version (the hosted audit records it as `server_version`). */
|
|
10
|
+
export declare const CLI_SCRIPTS_VERSION: string;
|
|
6
11
|
export declare const PITCHER_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiI+PHJlY3Qgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiIHJ4PSIxMTYiIGZpbGw9IiMyOTMzNUMiLz48Y2lyY2xlIGN4PSIzNzIiIGN5PSIxNTAiIHI9IjMwIiBmaWxsPSIjQ0MwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTE1MCAxMTZoOTZhOTIgOTIgMCAwIDEgMCAxODRoLTUwdjk2aC00NnoiLz48cGF0aCBmaWxsPSIjMjkzMzVDIiBkPSJNMTk2IDE2MHY5Nmg1MGE0OCA0OCAwIDAgMCAwLTk2eiIvPjwvc3ZnPg==";
|
|
7
12
|
/**
|
|
8
13
|
* The per-server request context: who every tool call runs as, and against which origin.
|
|
@@ -22,5 +27,62 @@ export interface BuildServerOptions {
|
|
|
22
27
|
* is a build option, never a per-call field. Stdio passes `env.prod`; hosted always passes true.
|
|
23
28
|
*/
|
|
24
29
|
destructiveWrites?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* MCP Apps (PIT-10066): the views for this request (all of them when the org's flag is on). Unset (local p mcp,
|
|
32
|
+
* hosted with the flag off) = every `ui` tool is skipped and instructions are unchanged, so the server is exactly today's.
|
|
33
|
+
*/
|
|
34
|
+
apps?: {
|
|
35
|
+
views: AppViewDescriptor[];
|
|
36
|
+
loadHtml: (viewName: string) => string;
|
|
37
|
+
/** PIT-10113: the proposal signer for this request. Absent = no confirm tool is registered. */
|
|
38
|
+
proposals?: ProposalSigner;
|
|
39
|
+
/** PIT-10113: this request's host client class (chooses the proposal text tail only). */
|
|
40
|
+
hostClient?: HostClient;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* PIT-10114: the org's tool policy. Hosted builds register only allowed tools already; this is defence in depth: a
|
|
44
|
+
* tool the policy forbids is refused (FORBIDDEN) without running. Unset (local p mcp) = no check.
|
|
45
|
+
*/
|
|
46
|
+
policy?: ToolPolicy;
|
|
47
|
+
/** PIT-10114: one event per tool call. Unset = no-op (local p mcp). */
|
|
48
|
+
onToolCall?: ToolCallSink;
|
|
49
|
+
}
|
|
50
|
+
/** PIT-10114: one event per tool call. Ids only: never arguments, results, names or emails. */
|
|
51
|
+
export interface ToolCallEvent {
|
|
52
|
+
tool: string;
|
|
53
|
+
group: ToolGroup;
|
|
54
|
+
/** accessOf(descriptor): the explicit class, never readOnlyHint */
|
|
55
|
+
access: ToolAccess;
|
|
56
|
+
outcome: 'ok' | 'error' | 'denied';
|
|
57
|
+
/** '' on ok; the CliError code (envelope or thrown), UNKNOWN for a thrown non-CliError, or
|
|
58
|
+
* INVALID_ARGS | FORBIDDEN | GROUP_INACTIVE | GROUP_READ_ONLY | APP_DISABLED */
|
|
59
|
+
errorCode: string;
|
|
60
|
+
/** epoch ms when the call started (before run(); receipt time for denied and INVALID_ARGS) */
|
|
61
|
+
startedAt: number;
|
|
62
|
+
/** run() only; 0 for denied and INVALID_ARGS */
|
|
63
|
+
latencyMs: number;
|
|
64
|
+
/** HTTP status of the backend's error answer when the failure came from a backend response; 0 otherwise */
|
|
65
|
+
backendStatus: number;
|
|
66
|
+
/** the call's instanceId argument when it is a plain id token, else '' */
|
|
67
|
+
instanceId: string;
|
|
68
|
+
/** descriptor ui.visibility is exactly ['app'] */
|
|
69
|
+
viaApp: boolean;
|
|
70
|
+
/** PIT-10113 confirm tools only */
|
|
71
|
+
confirm?: {
|
|
72
|
+
role: 'propose' | 'commit';
|
|
73
|
+
action: string;
|
|
74
|
+
proposalId?: string;
|
|
75
|
+
status?: 'proposed' | 'committed' | 'already_committed' | 'refused';
|
|
76
|
+
};
|
|
25
77
|
}
|
|
78
|
+
export type ToolCallSink = (e: ToolCallEvent) => void | Promise<void>;
|
|
79
|
+
/** The event, built field by field (never by spreading args or the envelope). */
|
|
80
|
+
export declare function toolCallEvent(tool: ToolDescriptor, args: Record<string, unknown>, r: {
|
|
81
|
+
outcome: ToolCallEvent['outcome'];
|
|
82
|
+
errorCode: string;
|
|
83
|
+
startedAt: number;
|
|
84
|
+
latencyMs: number;
|
|
85
|
+
backendStatus: number;
|
|
86
|
+
data?: unknown;
|
|
87
|
+
}): ToolCallEvent | undefined;
|
|
26
88
|
export declare function buildServer(ctx: McpRunContext, tools?: ToolDescriptor[], prompts?: PromptDescriptor[], resources?: ResourceDescriptor[], opts?: BuildServerOptions): McpServer;
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from '@modelcontextprotocol/ext-apps/server';
|
|
2
3
|
import { readFileSync } from 'fs';
|
|
3
4
|
import { dirname, join } from 'path';
|
|
4
5
|
import { fileURLToPath } from 'url';
|
|
5
6
|
import { allTools } from './registry.js';
|
|
6
7
|
import { allPrompts } from './prompts/registry.js';
|
|
7
8
|
import { allResources } from './resources/registry.js';
|
|
8
|
-
import { envelopeToToolResult, toErrorResult } from './result.js';
|
|
9
|
-
import { ANALYTICS_RENDER_POLICY } from './renderPolicy.js';
|
|
9
|
+
import { envelopeToToolResult, MAX_RESULT_BYTES, toErrorResult } from './result.js';
|
|
10
|
+
import { ANALYTICS_RENDER_POLICY, APPS_RENDER_POLICY } from './renderPolicy.js';
|
|
11
|
+
import { appViewUri } from './apps/types.js';
|
|
10
12
|
import { isCliError } from '../core/errors.js';
|
|
11
|
-
|
|
13
|
+
import { accessOf, toolVerdict } from './groups.js';
|
|
14
|
+
/** The package version (the hosted audit records it as `server_version`). */
|
|
15
|
+
export const CLI_SCRIPTS_VERSION = (() => {
|
|
12
16
|
try {
|
|
13
17
|
const dir = dirname(fileURLToPath(import.meta.url));
|
|
14
18
|
return JSON.parse(readFileSync(join(dir, '..', '..', '..', 'package.json'), 'utf8')).version ?? '0.0.0';
|
|
@@ -28,10 +32,79 @@ function errorText(e) {
|
|
|
28
32
|
return `${e.code}: ${e.message}`;
|
|
29
33
|
return e instanceof Error ? e.message : String(e);
|
|
30
34
|
}
|
|
35
|
+
const PLAIN_ID = /^[A-Za-z0-9_-]{1,200}$/;
|
|
36
|
+
const CONFIRM_STATUSES = new Set(['proposed', 'committed', 'already_committed', 'refused']);
|
|
37
|
+
/** A sink's throw or rejection never reaches the tool result. */
|
|
38
|
+
function emitSafe(sink, e) {
|
|
39
|
+
if (!sink)
|
|
40
|
+
return Promise.resolve();
|
|
41
|
+
try {
|
|
42
|
+
return Promise.resolve(sink(e)).then(() => undefined, () => undefined);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return Promise.resolve();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** The event, built field by field (never by spreading args or the envelope). */
|
|
49
|
+
export function toolCallEvent(tool, args, r) {
|
|
50
|
+
if (!tool.group)
|
|
51
|
+
return undefined; // every hosted descriptor has one (checkDescriptor); nothing to attribute otherwise
|
|
52
|
+
const instanceId = typeof args.instanceId === 'string' && PLAIN_ID.test(args.instanceId) ? args.instanceId : '';
|
|
53
|
+
const v = tool.ui?.visibility;
|
|
54
|
+
const event = {
|
|
55
|
+
tool: tool.name,
|
|
56
|
+
group: tool.group,
|
|
57
|
+
access: accessOf(tool),
|
|
58
|
+
outcome: r.outcome,
|
|
59
|
+
errorCode: r.errorCode,
|
|
60
|
+
startedAt: r.startedAt,
|
|
61
|
+
latencyMs: r.latencyMs,
|
|
62
|
+
backendStatus: r.backendStatus,
|
|
63
|
+
instanceId,
|
|
64
|
+
viaApp: Array.isArray(v) && v.length === 1 && v[0] === 'app',
|
|
65
|
+
};
|
|
66
|
+
if (tool.confirm) {
|
|
67
|
+
const d = r.data && typeof r.data === 'object' ? r.data : {};
|
|
68
|
+
const proposalId = typeof d.proposalId === 'string' ? d.proposalId : undefined;
|
|
69
|
+
const status = r.outcome === 'ok'
|
|
70
|
+
? typeof d.status === 'string' && CONFIRM_STATUSES.has(d.status)
|
|
71
|
+
? d.status
|
|
72
|
+
: undefined
|
|
73
|
+
: 'refused';
|
|
74
|
+
event.confirm = {
|
|
75
|
+
role: tool.confirm.role,
|
|
76
|
+
action: tool.confirm.action,
|
|
77
|
+
...(proposalId !== undefined ? { proposalId } : {}),
|
|
78
|
+
...(status !== undefined ? { status } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return event;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Tool `_meta` for a `ui` tool. Keys come from ext-apps so they cannot drift. PIT-10113 adds the legacy OpenAI keys, belt
|
|
85
|
+
* and braces for ChatGPT: an app-only tool is `openai/visibility: private`, and every tool a view calls (app-only tools,
|
|
86
|
+
* and a propose tool, which the confirm view re-calls after an edit) is `openai/widgetAccessible`.
|
|
87
|
+
*/
|
|
88
|
+
function uiMeta(tool) {
|
|
89
|
+
const { ui } = tool;
|
|
90
|
+
const resourceUri = appViewUri(ui.view);
|
|
91
|
+
const appOnly = ui.visibility?.length === 1 && ui.visibility[0] === 'app';
|
|
92
|
+
return {
|
|
93
|
+
ui: { resourceUri, ...(ui.visibility ? { visibility: ui.visibility } : {}) },
|
|
94
|
+
[RESOURCE_URI_META_KEY]: resourceUri, // legacy key, older hosts
|
|
95
|
+
...(appOnly ? { 'openai/visibility': 'private' } : {}),
|
|
96
|
+
...(appOnly || tool.confirm?.role === 'propose' ? { 'openai/widgetAccessible': true } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
31
99
|
export function buildServer(ctx, tools = allTools, prompts = allPrompts, resources = allResources, opts = {}) {
|
|
100
|
+
const enabled = new Set((opts.apps?.views ?? []).map((v) => v.name));
|
|
101
|
+
const instructions = enabled.size ? `${ANALYTICS_RENDER_POLICY}\n\n${APPS_RENDER_POLICY}` : ANALYTICS_RENDER_POLICY;
|
|
102
|
+
// A `ui` tool is registered only when its view is enabled for this server (D5); a confirm tool only with a signer.
|
|
103
|
+
const registered = tools.filter((t) => (!t.ui || enabled.has(t.ui.view)) && (!t.confirm || opts.apps?.proposals !== undefined));
|
|
104
|
+
const availableTools = new Set(registered.map((t) => t.name));
|
|
32
105
|
const server = new McpServer({
|
|
33
106
|
name: 'pitcher',
|
|
34
|
-
version,
|
|
107
|
+
version: CLI_SCRIPTS_VERSION,
|
|
35
108
|
title: 'Pitcher',
|
|
36
109
|
description: 'Search, manage, and share Pitcher content, canvases, files, DSRs, and instances.',
|
|
37
110
|
websiteUrl: 'https://www.pitcher.com',
|
|
@@ -39,27 +112,79 @@ export function buildServer(ctx, tools = allTools, prompts = allPrompts, resourc
|
|
|
39
112
|
},
|
|
40
113
|
// Surfaced to the host model via InitializeResult.instructions — the analytics answer-rendering
|
|
41
114
|
// policy so a natural-language question gets a right-sized reply (sentence / table / chart /
|
|
42
|
-
// dashboard) instead of a rigid full dashboard every time. See renderPolicy.ts.
|
|
43
|
-
|
|
44
|
-
|
|
115
|
+
// dashboard) instead of a rigid full dashboard every time. See renderPolicy.ts. The apps paragraph rides
|
|
116
|
+
// along only when a view is enabled.
|
|
117
|
+
{ instructions });
|
|
118
|
+
for (const tool of registered) {
|
|
45
119
|
const destructive = tool.alwaysDestructive === true || (tool.write === true && opts.destructiveWrites === true);
|
|
46
120
|
server.registerTool(tool.name, {
|
|
47
121
|
title: tool.title,
|
|
48
122
|
description: tool.description,
|
|
49
123
|
inputSchema: tool.inputShape,
|
|
50
124
|
annotations: { readOnlyHint: tool.readOnly, destructiveHint: destructive },
|
|
125
|
+
...(tool.ui ? { _meta: uiMeta({ ...tool, ui: tool.ui }) } : {}),
|
|
51
126
|
}, async (args) => {
|
|
127
|
+
const startedAt = Date.now();
|
|
128
|
+
// Defence in depth (PIT-10114): a hosted build never registers a forbidden tool; refuse it anyway, never run it.
|
|
129
|
+
if (opts.policy && toolVerdict(opts.policy, tool) !== 'allowed') {
|
|
130
|
+
const denied = toolCallEvent(tool, args, {
|
|
131
|
+
outcome: 'denied',
|
|
132
|
+
errorCode: 'FORBIDDEN',
|
|
133
|
+
startedAt,
|
|
134
|
+
latencyMs: 0,
|
|
135
|
+
backendStatus: 0,
|
|
136
|
+
});
|
|
137
|
+
if (denied)
|
|
138
|
+
void emitSafe(opts.onToolCall, denied); // nothing ran: never awaited
|
|
139
|
+
return toErrorResult('FORBIDDEN', 'This Pitcher tool is turned off for your organization.');
|
|
140
|
+
}
|
|
141
|
+
let result;
|
|
142
|
+
let outcome = 'ok';
|
|
143
|
+
let errorCode = '';
|
|
144
|
+
let backendStatus = 0;
|
|
145
|
+
let data;
|
|
52
146
|
try {
|
|
53
147
|
const credential = ctx.resolveCredential ? await ctx.resolveCredential() : ctx.credential;
|
|
54
148
|
// spread args FIRST so injected auth/env always wins — never reorder to { credential, ...args }
|
|
55
|
-
|
|
56
|
-
|
|
149
|
+
// `availableTools`, `proposals` and `hostClient` reach `ui` tools only (D7); every other tool gets exactly today's keys
|
|
150
|
+
const injected = {
|
|
151
|
+
credential,
|
|
152
|
+
org: ctx.org,
|
|
153
|
+
url: ctx.url,
|
|
154
|
+
...(tool.ui ? { availableTools, proposals: opts.apps?.proposals, hostClient: opts.apps?.hostClient } : {}),
|
|
155
|
+
};
|
|
156
|
+
const envelope = await tool.run({ ...args, ...injected });
|
|
157
|
+
if (!envelope.ok) {
|
|
158
|
+
outcome = 'error';
|
|
159
|
+
errorCode = envelope.error?.code ?? 'UNKNOWN';
|
|
160
|
+
backendStatus = envelope.error?.backendStatus ?? 0;
|
|
161
|
+
}
|
|
162
|
+
data = envelope.data;
|
|
163
|
+
result = envelopeToToolResult(envelope, MAX_RESULT_BYTES, tool.ui ? tool.toText : undefined, tool.ui ? tool.textMaxBytes : undefined);
|
|
57
164
|
}
|
|
58
165
|
catch (e) {
|
|
59
166
|
const code = isCliError(e) ? e.code : 'UNKNOWN';
|
|
60
167
|
const message = isCliError(e) ? e.message : e instanceof Error ? e.message : String(e);
|
|
61
|
-
|
|
168
|
+
outcome = 'error';
|
|
169
|
+
errorCode = code;
|
|
170
|
+
backendStatus = isCliError(e) ? (e.backendStatus ?? 0) : 0;
|
|
171
|
+
result = toErrorResult(code, message);
|
|
172
|
+
}
|
|
173
|
+
const event = toolCallEvent(tool, args, {
|
|
174
|
+
outcome,
|
|
175
|
+
errorCode,
|
|
176
|
+
startedAt,
|
|
177
|
+
latencyMs: Date.now() - startedAt,
|
|
178
|
+
backendStatus,
|
|
179
|
+
data,
|
|
180
|
+
});
|
|
181
|
+
if (event) {
|
|
182
|
+
const sent = emitSafe(opts.onToolCall, event);
|
|
183
|
+
// Write class waits for the audit attempt (the sink bounds itself); reads never do (G11).
|
|
184
|
+
if (event.access === 'write')
|
|
185
|
+
await sent;
|
|
62
186
|
}
|
|
187
|
+
return result;
|
|
63
188
|
});
|
|
64
189
|
}
|
|
65
190
|
for (const prompt of prompts) {
|
|
@@ -85,5 +210,31 @@ export function buildServer(ctx, tools = allTools, prompts = allPrompts, resourc
|
|
|
85
210
|
}
|
|
86
211
|
});
|
|
87
212
|
}
|
|
213
|
+
const apps = opts.apps;
|
|
214
|
+
for (const view of apps?.views ?? []) {
|
|
215
|
+
server.registerResource(view.name, appViewUri(view.name), { title: view.title, description: view.description, mimeType: RESOURCE_MIME_TYPE }, (uri) => {
|
|
216
|
+
try {
|
|
217
|
+
return {
|
|
218
|
+
contents: [
|
|
219
|
+
{
|
|
220
|
+
uri: uri.href,
|
|
221
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
222
|
+
text: apps.loadHtml(view.name),
|
|
223
|
+
_meta: {
|
|
224
|
+
ui: {
|
|
225
|
+
csp: view.csp?.(ctx) ?? {},
|
|
226
|
+
...(view.permissions ? { permissions: view.permissions } : {}),
|
|
227
|
+
prefersBorder: view.prefersBorder ?? true,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
throw new Error(`app view "${view.name}" failed: ${errorText(e)}`);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
88
239
|
return server;
|
|
89
240
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { KPI_TYPES } from '../../core/kpiTypes.js';
|
|
3
|
-
import { runAnalyticsOrg, runAnalyticsInstance, runAnalyticsKpi, runAnalyticsEngagement, runAnalyticsPopularity, runAnalyticsUsers, runAnalyticsFiles, runAnalyticsCanvases, runAnalyticsViewers, runAnalyticsSessions, } from '../../commands/analytics.js';
|
|
3
|
+
import { runAnalyticsOrg, runAnalyticsInstance, runAnalyticsKpi, runAnalyticsEngagement, runAnalyticsPopularity, runAnalyticsUsers, runAnalyticsFiles, runAnalyticsCanvases, runAnalyticsViewers, runAnalyticsSessions, runAnalyticsFileUsers, runAnalyticsFilePages, runAnalyticsCanvasUsers, runAnalyticsViewerVisits, } from '../../commands/analytics.js';
|
|
4
4
|
export const analyticsTools = [
|
|
5
5
|
{
|
|
6
6
|
name: 'pitcher_analytics_org',
|
|
@@ -23,7 +23,7 @@ export const analyticsTools = [
|
|
|
23
23
|
title: 'Instance KPI metrics',
|
|
24
24
|
description: 'Read KPI metrics for an instance from the clickhouse analytics service (PIT-7647). ' +
|
|
25
25
|
'Access follows the signed-in user’s own role; activity-metric types need start/end dates ' +
|
|
26
|
-
'(inventory counts like total-members do not).',
|
|
26
|
+
'(inventory counts like total-members do not). Every value and growth figure is a number.',
|
|
27
27
|
inputShape: {
|
|
28
28
|
instanceId: z.string().describe('Instance ID (required)'),
|
|
29
29
|
type: z.enum(KPI_TYPES).describe('KPI type'),
|
|
@@ -97,7 +97,9 @@ export const analyticsTools = [
|
|
|
97
97
|
name: 'pitcher_analytics_files',
|
|
98
98
|
title: 'File usage analytics',
|
|
99
99
|
description: 'File usage analytics for an instance (views/opens/engagement per file), summarized and capped to ' +
|
|
100
|
-
'`limit`. Requires an org-admin key. Defaults to a 30-day window when dates are omitted.'
|
|
100
|
+
'`limit`. Requires an org-admin key. Defaults to a 30-day window when dates are omitted. ' +
|
|
101
|
+
'Pass userEmails to answer "which files did this user open"; for "who opened this file" use ' +
|
|
102
|
+
'pitcher_analytics_file_users.',
|
|
101
103
|
inputShape: {
|
|
102
104
|
instanceId: z.string().describe('Instance ID (required)'),
|
|
103
105
|
startDate: z.string().optional().describe('Window start, ISO date (defaults to 30 days ago)'),
|
|
@@ -173,4 +175,87 @@ export const analyticsTools = [
|
|
|
173
175
|
run: runAnalyticsSessions,
|
|
174
176
|
readOnly: true,
|
|
175
177
|
},
|
|
178
|
+
{
|
|
179
|
+
name: 'pitcher_analytics_file_users',
|
|
180
|
+
title: 'Who opened a file',
|
|
181
|
+
description: 'Who opened ONE file in an instance: per user (email, name, role) the opens, seconds, days active and the ' +
|
|
182
|
+
'last day opened, sorted by opens and capped to `limit` (total_count/has_more state the cap). Internal users ' +
|
|
183
|
+
'only. pageNumber (0-based) narrows to one page of the file. Needs an instance editor, instance admin or org ' +
|
|
184
|
+
'admin. Defaults to a 30-day window. For time per page use pitcher_analytics_file_pages.',
|
|
185
|
+
inputShape: {
|
|
186
|
+
instanceId: z.string().describe('Instance ID (required)'),
|
|
187
|
+
fileId: z.string().describe('File ID (required)'),
|
|
188
|
+
startDate: z.string().optional().describe('Window start, ISO date (defaults to 30 days ago)'),
|
|
189
|
+
endDate: z.string().optional().describe('Window end, ISO date (defaults to today)'),
|
|
190
|
+
pageNumber: z.number().int().min(0).optional().describe('Only this page of the file (0-based)'),
|
|
191
|
+
groupIds: z.string().optional().describe('Comma-separated distribution-group IDs'),
|
|
192
|
+
userEmails: z.string().optional().describe('Comma-separated user emails to scope by'),
|
|
193
|
+
limit: z.number().int().optional().describe('Max rows to return (default 20, max 100)'),
|
|
194
|
+
},
|
|
195
|
+
run: runAnalyticsFileUsers,
|
|
196
|
+
readOnly: true,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'pitcher_analytics_file_pages',
|
|
200
|
+
title: 'Time per page of a file',
|
|
201
|
+
description: 'Time per page of ONE file: per page (1-based page_number) the internal opens, internal seconds and mean ' +
|
|
202
|
+
'seconds, plus external (DSR) views. Pass a single userEmails value for one user\'s time per page. Every page ' +
|
|
203
|
+
'is returned. Needs an instance editor, instance admin or org admin. Defaults to a 30-day window.',
|
|
204
|
+
inputShape: {
|
|
205
|
+
instanceId: z.string().describe('Instance ID (required)'),
|
|
206
|
+
fileId: z.string().describe('File ID (required)'),
|
|
207
|
+
startDate: z.string().optional().describe('Window start, ISO date (defaults to 30 days ago)'),
|
|
208
|
+
endDate: z.string().optional().describe('Window end, ISO date (defaults to today)'),
|
|
209
|
+
groupIds: z.string().optional().describe('Comma-separated distribution-group IDs'),
|
|
210
|
+
userEmails: z.string().optional().describe('Comma-separated user emails to scope by (one email: that user)'),
|
|
211
|
+
},
|
|
212
|
+
run: runAnalyticsFilePages,
|
|
213
|
+
readOnly: true,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'pitcher_analytics_canvas_users',
|
|
217
|
+
title: 'Per-user canvas usage',
|
|
218
|
+
description: 'Per-user canvas usage in an instance: one row per user and canvas (who, canvas_id, canvas_name, views = ' +
|
|
219
|
+
'visits, total_seconds, last_viewed_at), most visits first, `limit` rows per page with total_count/has_more. ' +
|
|
220
|
+
'With canvasId, each row also lists the pages (sections) of that canvas the user viewed with their seconds. ' +
|
|
221
|
+
'Internal users only (external DSR viewers: pitcher_analytics_viewer_visits); presentation time only, not ' +
|
|
222
|
+
'editing. Needs an instance editor, instance admin or org admin. Defaults to a 30-day window.',
|
|
223
|
+
inputShape: {
|
|
224
|
+
instanceId: z.string().describe('Instance ID (required)'),
|
|
225
|
+
canvasId: z.string().optional().describe("One canvas: adds each user's seconds per page (section)"),
|
|
226
|
+
startDate: z.string().optional().describe('Window start, ISO date (defaults to 30 days ago)'),
|
|
227
|
+
endDate: z.string().optional().describe('Window end, ISO date (defaults to today)'),
|
|
228
|
+
groupIds: z.string().optional().describe('Comma-separated distribution-group IDs'),
|
|
229
|
+
userEmails: z.string().optional().describe('Comma-separated user emails to scope by'),
|
|
230
|
+
page: z.number().int().optional().describe('Page number (default 1)'),
|
|
231
|
+
limit: z.number().int().optional().describe('Rows per page (default 20, max 100)'),
|
|
232
|
+
},
|
|
233
|
+
run: runAnalyticsCanvasUsers,
|
|
234
|
+
readOnly: true,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'pitcher_analytics_viewer_visits',
|
|
238
|
+
title: 'DSR viewer visits and page time',
|
|
239
|
+
description: 'Per viewer of a DSR canvas: each visit (start, engaged seconds) and in it the sections, files and pages ' +
|
|
240
|
+
'viewed with views and seconds (`estimated: true` where an exit event was missing and the time is ' +
|
|
241
|
+
'inferred). One viewer (userEmail, verbatim from pitcher_analytics_viewers) or the most recently active ' +
|
|
242
|
+
'viewers (limit, default 10, max 25; viewers_total and truncated state the cap). format "csv" returns the ' +
|
|
243
|
+
'same as CSV text in `csv` (one row per viewer, visit and item) with a suggested filename, for export: hand ' +
|
|
244
|
+
'it to the user as a file. Works for any member. The server applies its own default window.',
|
|
245
|
+
inputShape: {
|
|
246
|
+
instanceId: z.string().describe('Instance ID (required)'),
|
|
247
|
+
canvasId: z.string().describe('DSR canvas ID (required)'),
|
|
248
|
+
userEmail: z
|
|
249
|
+
.string()
|
|
250
|
+
.optional()
|
|
251
|
+
.describe('One viewer: normalized_email (or email) from pitcher_analytics_viewers, VERBATIM'),
|
|
252
|
+
viewerType: z.string().optional().describe("Viewer type when listing: all, internal, external (default 'all')"),
|
|
253
|
+
startDate: z.string().optional().describe('Window start, ISO date (server default applies if omitted)'),
|
|
254
|
+
endDate: z.string().optional().describe('Window end, ISO date (server default applies if omitted)'),
|
|
255
|
+
limit: z.number().int().optional().describe('Viewers to read when no userEmail (default 10, max 25)'),
|
|
256
|
+
format: z.enum(['json', 'csv']).optional().describe('json (default) or csv for an export'),
|
|
257
|
+
},
|
|
258
|
+
run: runAnalyticsViewerVisits,
|
|
259
|
+
readOnly: true,
|
|
260
|
+
},
|
|
176
261
|
];
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ROLEPLAY_DIFFICULTIES, ROLEPLAY_GROUP_BY, ROLEPLAY_PATH_STATUSES, ROLEPLAY_SESSION_SORT, runRoleplayKpis, runRoleplayPathsList, runRoleplayRepSummary, runRoleplayScenariosList, runRoleplaySessionGet, runRoleplaySessions, runRoleplayTeam, } from '../../commands/roleplay.js';
|
|
3
|
+
const instanceId = z.string().describe('Instance ID (required)');
|
|
4
|
+
/** The shared roleplay_statements filters. Dates have NO default window: omitted means all time. */
|
|
5
|
+
const filters = {
|
|
6
|
+
instanceId,
|
|
7
|
+
startDate: z.string().optional().describe('Window start, ISO date. Omitted = all time (no default window)'),
|
|
8
|
+
endDate: z.string().optional().describe('Window end, ISO date. Omitted = all time (no default window)'),
|
|
9
|
+
rep: z.string().optional().describe('Rep name or email contains (case-insensitive)'),
|
|
10
|
+
scenario: z.string().optional().describe('Scenario or persona name contains (case-insensitive)'),
|
|
11
|
+
difficulty: z.enum(ROLEPLAY_DIFFICULTIES).optional().describe('Scenario difficulty'),
|
|
12
|
+
};
|
|
13
|
+
const UNSCORED = 'A session without a score (score null: the AI evaluation failed or is pending) is UNSCORED, never 0: ' +
|
|
14
|
+
'never count it in an average or as a fail.';
|
|
15
|
+
const CH_SOURCE = 'Reads the AI Roleplay analytics replica (ClickHouse roleplay_statements, the Admin Analytics tab). ' +
|
|
16
|
+
'Dates are optional and there is no default window: omitted means all time.';
|
|
17
|
+
const ADMIN_ONLY = 'Instance or org admins only; anyone else gets an AUTH error, and a coaching manager should use pitcher_roleplay_team.';
|
|
18
|
+
export const roleplayTools = [
|
|
19
|
+
{
|
|
20
|
+
name: 'pitcher_roleplay_kpis',
|
|
21
|
+
title: 'AI Roleplay KPIs',
|
|
22
|
+
description: 'AI Roleplay totals for an instance: total, scored and unscored sessions, the mean score of SCORED sessions, ' +
|
|
23
|
+
`mean duration and active reps, with optional rep/scenario/difficulty filters. ${UNSCORED} ${CH_SOURCE} ${ADMIN_ONLY}`,
|
|
24
|
+
inputShape: filters,
|
|
25
|
+
run: runRoleplayKpis,
|
|
26
|
+
readOnly: true,
|
|
27
|
+
group: 'roleplay',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'pitcher_roleplay_sessions',
|
|
31
|
+
title: 'AI Roleplay sessions',
|
|
32
|
+
description: 'AI Roleplay session list for an instance, newest first: entry_id, session_at, rep_name, who (rep email), ' +
|
|
33
|
+
'scenario_name, persona_name, difficulty, score, evaluated, rating, duration_ms. Default 20 rows, max 100 per ' +
|
|
34
|
+
`page; total_count/has_more say when there are more. ${UNSCORED} ${CH_SOURCE} ${ADMIN_ONLY} ` +
|
|
35
|
+
'Open one session with pitcher_roleplay_session_get (its entry_id).',
|
|
36
|
+
inputShape: {
|
|
37
|
+
...filters,
|
|
38
|
+
sortBy: z.enum(ROLEPLAY_SESSION_SORT).optional().describe('Sort field (default session_at)'),
|
|
39
|
+
sortOrder: z.enum(['asc', 'desc']).optional().describe('Sort direction (default desc); unscored sessions sort last'),
|
|
40
|
+
page: z.number().int().optional().describe('Page number (default 1)'),
|
|
41
|
+
limit: z.number().int().optional().describe('Rows per page (default 20, max 100)'),
|
|
42
|
+
},
|
|
43
|
+
run: runRoleplaySessions,
|
|
44
|
+
readOnly: true,
|
|
45
|
+
group: 'roleplay',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'pitcher_roleplay_rep_summary',
|
|
49
|
+
title: 'AI Roleplay per-rep or per-scenario summary',
|
|
50
|
+
description: 'Per-rep (groupBy rep, keyed on the rep email) or per-scenario (groupBy scenario) session counts, scored ' +
|
|
51
|
+
'counts, mean score of scored sessions and last session, up to 50 groups with the most sessions. Exact up to ' +
|
|
52
|
+
'5000 sessions in the window (read newest first); `complete: false` means the figures cover only the newest ' +
|
|
53
|
+
`5000, so narrow the dates. ${UNSCORED} ${CH_SOURCE} ${ADMIN_ONLY}`,
|
|
54
|
+
inputShape: {
|
|
55
|
+
...filters,
|
|
56
|
+
groupBy: z.enum(ROLEPLAY_GROUP_BY).optional().describe('Group by rep (default) or scenario'),
|
|
57
|
+
},
|
|
58
|
+
run: runRoleplayRepSummary,
|
|
59
|
+
readOnly: true,
|
|
60
|
+
group: 'roleplay',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'pitcher_roleplay_team',
|
|
64
|
+
title: 'AI Roleplay assignment completion',
|
|
65
|
+
description: 'Roleplay assignment completion per rep, as the AI Roleplay manager app shows it: per assignment its passing ' +
|
|
66
|
+
'score and due date, each rep\'s status (passed, in_progress, not_started, overdue, needs_coaching), sessions, ' +
|
|
67
|
+
'scored sessions, best and mean score, trend, last session, plus per-skill mean scores. Instance or org admins ' +
|
|
68
|
+
'get every roleplay assignment on the instance (scope "instance"); a coaching manager gets the assignments ' +
|
|
69
|
+
'they coach (scope "coach"). Works for any member (an empty list when they coach nobody). ' +
|
|
70
|
+
`${UNSCORED} Reads Postgres appsDB through next-core (cch-assignment, cch-session-result), not ClickHouse. ` +
|
|
71
|
+
'The server returns at most the newest 1000 sessions; `complete: false` says the cap was hit. ' +
|
|
72
|
+
'Up to 50 assignments and 100 reps each; filter with assignmentId or status. ' +
|
|
73
|
+
'sessions and best_score count every attempt since the assignment started (server pass progress); ' +
|
|
74
|
+
'recent_only true means avg_score, scored_sessions and trend cover only the newest listed sessions; ' +
|
|
75
|
+
'coverage says whether pass progress was loaded; assignments are ordered active first, then by due date, ' +
|
|
76
|
+
'and reps most urgent first.',
|
|
77
|
+
inputShape: {
|
|
78
|
+
instanceId,
|
|
79
|
+
assignmentId: z.string().optional().describe('Only this assignment (its id from an earlier call)'),
|
|
80
|
+
status: z
|
|
81
|
+
.enum(['passed', 'in_progress', 'not_started', 'overdue', 'needs_coaching'])
|
|
82
|
+
.optional()
|
|
83
|
+
.describe('Only reps with this status'),
|
|
84
|
+
},
|
|
85
|
+
run: runRoleplayTeam,
|
|
86
|
+
readOnly: true,
|
|
87
|
+
group: 'roleplay',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'pitcher_roleplay_session_get',
|
|
91
|
+
title: 'AI Roleplay session detail',
|
|
92
|
+
description: 'One AI Roleplay session: scenario, difficulty, time, duration, overall score (null = unscored, never 0), ' +
|
|
93
|
+
'evaluation status, and the evaluation summary, strengths, improvements and per-skill scores with feedback, ' +
|
|
94
|
+
'and the grading scale. ' +
|
|
95
|
+
'Never the transcript, messages or recordings. Opens to the rep, a coach of that rep, or an instance or org ' +
|
|
96
|
+
'admin; anyone else gets an AUTH error. Reads Postgres appsDB (cch-session-result) through next-core. ' +
|
|
97
|
+
'Takes entryId = entry_id from pitcher_roleplay_sessions (admins) or, where that tool is available, a session ' +
|
|
98
|
+
'id from pitcher_roleplay_rep_sessions (coaches).',
|
|
99
|
+
inputShape: {
|
|
100
|
+
instanceId,
|
|
101
|
+
entryId: z
|
|
102
|
+
.string()
|
|
103
|
+
.describe('Session entry ID (entry_id from pitcher_roleplay_sessions, or a session id from pitcher_roleplay_rep_sessions where available)'),
|
|
104
|
+
},
|
|
105
|
+
run: runRoleplaySessionGet,
|
|
106
|
+
readOnly: true,
|
|
107
|
+
group: 'roleplay',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'pitcher_roleplay_scenarios_list',
|
|
111
|
+
title: 'AI Roleplay scenarios',
|
|
112
|
+
description: 'The AI Roleplay scenario catalog of an instance: id, kind, name, persona title, description, difficulty, ' +
|
|
113
|
+
'active, time limit and the skills it is graded on (title and weight). Never a prompt. Any member. ' +
|
|
114
|
+
'Reads the instance config (appsDB cch-config) through next-core.',
|
|
115
|
+
inputShape: { instanceId },
|
|
116
|
+
run: runRoleplayScenariosList,
|
|
117
|
+
readOnly: true,
|
|
118
|
+
group: 'roleplay',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'pitcher_roleplay_paths_list',
|
|
122
|
+
title: 'AI Roleplay learning paths',
|
|
123
|
+
description: "The instance's AI Roleplay learning paths: id, name, status, version, stage and step counts, default passing " +
|
|
124
|
+
'score, last update. Default published only. Instance admins and editors and org admins; 404 when learning ' +
|
|
125
|
+
'paths are off on the instance or you are not a member.',
|
|
126
|
+
inputShape: {
|
|
127
|
+
instanceId,
|
|
128
|
+
status: z.enum(ROLEPLAY_PATH_STATUSES).optional().describe('published (default), draft or all'),
|
|
129
|
+
},
|
|
130
|
+
run: runRoleplayPathsList,
|
|
131
|
+
readOnly: true,
|
|
132
|
+
access: 'read',
|
|
133
|
+
group: 'roleplay',
|
|
134
|
+
},
|
|
135
|
+
];
|
|
@@ -1,7 +1,27 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { Envelope } from '../../core/envelope.js';
|
|
3
3
|
import type { CommonOpts } from '../../core/http.js';
|
|
4
|
+
import type { ToolAccess, ToolGroup } from '../groups.js';
|
|
5
|
+
import type { HostClient, ProposalSigner } from '../apps/confirm/types.js';
|
|
6
|
+
export type { ToolAccess, ToolGroup } from '../groups.js';
|
|
4
7
|
export type InjectedAuth = Pick<CommonOpts, 'apiKey' | 'credential' | 'org' | 'url'>;
|
|
8
|
+
export type AppToolVisibility = 'model' | 'app';
|
|
9
|
+
/** MCP Apps (PIT-10066): attach a view (`ui://pitcher/<view>.html`) to a tool's result. */
|
|
10
|
+
export interface ToolUi {
|
|
11
|
+
/** AppViewDescriptor.name, e.g. 'content-picker' -> ui://pitcher/content-picker.html */
|
|
12
|
+
view: string;
|
|
13
|
+
/** Default ['model', 'app']. ['app'] = app-only helper, hidden from the model by UI hosts. */
|
|
14
|
+
visibility?: AppToolVisibility[];
|
|
15
|
+
}
|
|
16
|
+
/** Extra keys buildServer injects into a `ui` tool's run (and ONLY into those). */
|
|
17
|
+
export interface AppInjected {
|
|
18
|
+
/** Names of the tools registered on THIS server (hosted: depends on the org's access mode). */
|
|
19
|
+
availableTools: ReadonlySet<string>;
|
|
20
|
+
/** PIT-10113: mints and opens proposal tokens for THIS request's caller and host client. Absent = no confirm tools. */
|
|
21
|
+
proposals?: ProposalSigner;
|
|
22
|
+
/** PIT-10113: the host client class of THIS request. Absent = `appsHost: false` (the 'host_prompt' text tail). */
|
|
23
|
+
hostClient?: HostClient;
|
|
24
|
+
}
|
|
5
25
|
export interface ToolDescriptor {
|
|
6
26
|
name: string;
|
|
7
27
|
title: string;
|
|
@@ -10,6 +30,21 @@ export interface ToolDescriptor {
|
|
|
10
30
|
run: (opts: Record<string, unknown> & InjectedAuth, deps?: any) => Promise<Envelope<unknown>>;
|
|
11
31
|
wraps?: (...a: any[]) => unknown;
|
|
12
32
|
readOnly: boolean;
|
|
33
|
+
/** PIT-10114: the tool group. Set by registry.ts (inGroup) or on the descriptor (app and confirm tools). */
|
|
34
|
+
group?: ToolGroup;
|
|
35
|
+
/** PIT-10114: policy class. 'write' = changes data OR only leads to a change (PIT-10113 _propose and _commit). Required for every new tool. */
|
|
36
|
+
access?: ToolAccess;
|
|
37
|
+
/** PIT-10113 confirm tools (propose / commit pair); registered only when a proposal signer exists. */
|
|
38
|
+
confirm?: {
|
|
39
|
+
role: 'propose' | 'commit';
|
|
40
|
+
action: string;
|
|
41
|
+
};
|
|
13
42
|
write?: boolean;
|
|
14
43
|
alwaysDestructive?: boolean;
|
|
44
|
+
/** MCP Apps: render this tool's result in a view. Registered ONLY when buildServer gets opts.apps with this view enabled. */
|
|
45
|
+
ui?: ToolUi;
|
|
46
|
+
/** `ui` tools only: the text block for the model (short); `structuredContent` carries the full data. */
|
|
47
|
+
toText?: (data: unknown) => string;
|
|
48
|
+
/** `ui` tools only: byte budget of the `toText` line (default 8,000). Only PIT-10113 propose tools raise it. */
|
|
49
|
+
textMaxBytes?: number;
|
|
15
50
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pitcher/cli-scripts",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "The Pitcher CLI for various tasks in its ecosystem.",
|
|
5
5
|
"author": "Pitcher AG",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/PitcherAG/web.git",
|
|
10
|
+
"directory": "packages/libs/cli-scripts"
|
|
11
|
+
},
|
|
7
12
|
"keywords": [
|
|
8
13
|
"pitcher",
|
|
9
14
|
"cli",
|
|
@@ -51,6 +56,7 @@
|
|
|
51
56
|
"vitest": "^5.0.1"
|
|
52
57
|
},
|
|
53
58
|
"dependencies": {
|
|
59
|
+
"@modelcontextprotocol/ext-apps": "1.7.5",
|
|
54
60
|
"@modelcontextprotocol/sdk": "^1",
|
|
55
61
|
"archiver": "^8.0.0",
|
|
56
62
|
"axios": "^1.18.1",
|