@pitcher/cli-scripts 3.2.0 → 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/dist/src/commands/dsrLanding.js +1 -1
- package/dist/src/commands/roleplay.d.ts +154 -3
- package/dist/src/commands/roleplay.js +276 -23
- package/dist/src/core/dsr-engagement.d.ts +152 -0
- package/dist/src/core/dsr-engagement.js +301 -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 +48 -7
- package/dist/src/core/roleplay-stats.js +68 -11
- 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 +29 -24
- package/dist/src/mcp/renderPolicy.d.ts +1 -0
- package/dist/src/mcp/renderPolicy.js +8 -2
- 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/roleplay.js +34 -5
- package/dist/src/mcp/tools/types.d.ts +35 -0
- package/package.json +2 -1
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,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { ROLEPLAY_DIFFICULTIES, ROLEPLAY_GROUP_BY, ROLEPLAY_SESSION_SORT, runRoleplayKpis, runRoleplayRepSummary, runRoleplayScenariosList, runRoleplaySessionGet, runRoleplaySessions, runRoleplayTeam, } from '../../commands/roleplay.js';
|
|
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
3
|
const instanceId = z.string().describe('Instance ID (required)');
|
|
4
4
|
/** The shared roleplay_statements filters. Dates have NO default window: omitted means all time. */
|
|
5
5
|
const filters = {
|
|
@@ -24,6 +24,7 @@ export const roleplayTools = [
|
|
|
24
24
|
inputShape: filters,
|
|
25
25
|
run: runRoleplayKpis,
|
|
26
26
|
readOnly: true,
|
|
27
|
+
group: 'roleplay',
|
|
27
28
|
},
|
|
28
29
|
{
|
|
29
30
|
name: 'pitcher_roleplay_sessions',
|
|
@@ -41,6 +42,7 @@ export const roleplayTools = [
|
|
|
41
42
|
},
|
|
42
43
|
run: runRoleplaySessions,
|
|
43
44
|
readOnly: true,
|
|
45
|
+
group: 'roleplay',
|
|
44
46
|
},
|
|
45
47
|
{
|
|
46
48
|
name: 'pitcher_roleplay_rep_summary',
|
|
@@ -55,6 +57,7 @@ export const roleplayTools = [
|
|
|
55
57
|
},
|
|
56
58
|
run: runRoleplayRepSummary,
|
|
57
59
|
readOnly: true,
|
|
60
|
+
group: 'roleplay',
|
|
58
61
|
},
|
|
59
62
|
{
|
|
60
63
|
name: 'pitcher_roleplay_team',
|
|
@@ -66,7 +69,11 @@ export const roleplayTools = [
|
|
|
66
69
|
'they coach (scope "coach"). Works for any member (an empty list when they coach nobody). ' +
|
|
67
70
|
`${UNSCORED} Reads Postgres appsDB through next-core (cch-assignment, cch-session-result), not ClickHouse. ` +
|
|
68
71
|
'The server returns at most the newest 1000 sessions; `complete: false` says the cap was hit. ' +
|
|
69
|
-
'Up to 50 assignments and 100 reps each; filter with assignmentId or status.'
|
|
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.',
|
|
70
77
|
inputShape: {
|
|
71
78
|
instanceId,
|
|
72
79
|
assignmentId: z.string().optional().describe('Only this assignment (its id from an earlier call)'),
|
|
@@ -77,21 +84,27 @@ export const roleplayTools = [
|
|
|
77
84
|
},
|
|
78
85
|
run: runRoleplayTeam,
|
|
79
86
|
readOnly: true,
|
|
87
|
+
group: 'roleplay',
|
|
80
88
|
},
|
|
81
89
|
{
|
|
82
90
|
name: 'pitcher_roleplay_session_get',
|
|
83
91
|
title: 'AI Roleplay session detail',
|
|
84
92
|
description: 'One AI Roleplay session: scenario, difficulty, time, duration, overall score (null = unscored, never 0), ' +
|
|
85
|
-
'evaluation status, and the evaluation summary, strengths, improvements and per-skill scores with feedback
|
|
93
|
+
'evaluation status, and the evaluation summary, strengths, improvements and per-skill scores with feedback, ' +
|
|
94
|
+
'and the grading scale. ' +
|
|
86
95
|
'Never the transcript, messages or recordings. Opens to the rep, a coach of that rep, or an instance or org ' +
|
|
87
96
|
'admin; anyone else gets an AUTH error. Reads Postgres appsDB (cch-session-result) through next-core. ' +
|
|
88
|
-
'Takes entryId = entry_id from pitcher_roleplay_sessions
|
|
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).',
|
|
89
99
|
inputShape: {
|
|
90
100
|
instanceId,
|
|
91
|
-
entryId: z
|
|
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)'),
|
|
92
104
|
},
|
|
93
105
|
run: runRoleplaySessionGet,
|
|
94
106
|
readOnly: true,
|
|
107
|
+
group: 'roleplay',
|
|
95
108
|
},
|
|
96
109
|
{
|
|
97
110
|
name: 'pitcher_roleplay_scenarios_list',
|
|
@@ -102,5 +115,21 @@ export const roleplayTools = [
|
|
|
102
115
|
inputShape: { instanceId },
|
|
103
116
|
run: runRoleplayScenariosList,
|
|
104
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',
|
|
105
134
|
},
|
|
106
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,6 +1,6 @@
|
|
|
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",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"vitest": "^5.0.1"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
+
"@modelcontextprotocol/ext-apps": "1.7.5",
|
|
59
60
|
"@modelcontextprotocol/sdk": "^1",
|
|
60
61
|
"archiver": "^8.0.0",
|
|
61
62
|
"axios": "^1.18.1",
|