@pitcher/cli-scripts 3.0.1 → 3.1.1
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 +3 -3
- package/dist/src/commands/metadataTemplate.js +3 -2
- package/dist/src/commands/scaffolds.js +0 -6
- package/dist/src/core/pitcherAuth.d.ts +5 -1
- package/dist/src/core/pitcherAuth.js +13 -5
- package/dist/src/mcp/hosted.d.ts +15 -0
- package/dist/src/mcp/hosted.js +36 -3
- package/dist/src/mcp/index.d.ts +2 -2
- package/dist/src/mcp/index.js +2 -2
- package/dist/src/mcp/local.d.ts +1 -1
- package/dist/src/mcp/local.js +1 -1
- package/dist/src/mcp/prompts/analytics.js +4 -0
- package/dist/src/mcp/prompts/types.d.ts +1 -0
- package/dist/src/mcp/server.d.ts +1 -0
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/mcp/tools/metadataTemplate.js +3 -2
- package/dist/src/mcp/tools/types.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Releases are published to npm automatically: a merge to `main` that bumps `versi
|
|
|
6
6
|
|
|
7
7
|
> ⚠️ **Breaking changes in 3.0.0**
|
|
8
8
|
>
|
|
9
|
-
> 1. **Local `p mcp` now serves ONLY 3 tools** (`pitcher_dev_sync`, `pitcher_dev_sync_revert`, `pitcher_publish_app`), down from 124. Everything else is the hosted server `https://
|
|
9
|
+
> 1. **Local `p mcp` now serves ONLY 3 tools** (`pitcher_dev_sync`, `pitcher_dev_sync_revert`, `pitcher_publish_app`), down from 124. Everything else is the hosted server `https://mcp.pitcher.com/mcp`. `p mcp` no longer reads `NG_API_KEY`/`NG_ORG`/`NG_URL`/`NG_ENV`; run `p login` once (`--domain` or `PITCHER_DOMAIN` when logged in to several orgs).
|
|
10
10
|
> 2. **Every other command uses your `p login` session.** `--api-key` (automation) still wins when passed; `NG_API_KEY` is used only when you have NO `p login` session, and is otherwise ignored with a notice. `--org` no longer defaults to `dev` on the session path: the session's org is the target. `--domain` cannot be combined with `--api-key` or `--org`.
|
|
11
11
|
> 3. **The Claude Desktop `.mcpb` extension is retired.** Claude Desktop adds the remote URL natively.
|
|
12
12
|
> 4. **`p analytics kpi` requires `--type`** (one of the 12 KPI types listed in `p analytics kpi --help`). The old default `all` always returned a 500.
|
|
@@ -44,7 +44,7 @@ Pitcher's MCP tools come from two places:
|
|
|
44
44
|
|
|
45
45
|
| Server | Tools | Add it with |
|
|
46
46
|
|---|---|---|
|
|
47
|
-
| **Hosted** (OAuth; Claude Code, Claude Desktop, claude.ai, ChatGPT) | everything: content, canvases, DSRs, analytics, L&D, users, … (121 tools, prompts, resources) | `claude mcp add --transport http pitcher https://
|
|
47
|
+
| **Hosted** (OAuth; Claude Code, Claude Desktop, claude.ai, ChatGPT) | everything: content, canvases, DSRs, analytics, L&D, users, … (121 tools, prompts, resources) | `claude mcp add --transport http pitcher https://mcp.pitcher.com/mcp` — Claude Desktop: Settings → Connectors → Add custom connector, same URL |
|
|
48
48
|
| **Local** `p mcp` (stdio) | only `pitcher_dev_sync`, `pitcher_dev_sync_revert`, `pitcher_publish_app` (they zip a folder on YOUR machine) | `claude mcp add pitcher-local -- p mcp` |
|
|
49
49
|
|
|
50
50
|
Claude Code users add both. The local server runs on your `p login` session (no API key, no env block); the domain is fixed when it starts. Signed in to several orgs? Add one entry per org:
|
|
@@ -502,7 +502,7 @@ p metadata-template scaffold --out ./schema.json
|
|
|
502
502
|
p metadata-template upsert --instance-id 01... --sections-file ./schema.json --api-key myapikey
|
|
503
503
|
```
|
|
504
504
|
|
|
505
|
-
`upsert` merges the incoming sections by section `label` — a section whose label matches an existing one is replaced, other sections are preserved. It
|
|
505
|
+
`upsert` merges the incoming sections by section `label` — a section whose label matches an existing one is **replaced whole** (fields left out of it are removed), other sections are preserved. It cannot remove a whole section (that's a UI operation). `options` for `select`/`multi-select` fields are `{ label, value }` objects (bare strings are rejected). `upsert` requires an instance- or org-admin key; a non-admin member key is rejected (400 → `VALIDATION`).
|
|
506
506
|
|
|
507
507
|
## To run the `dsr` command:
|
|
508
508
|
|
|
@@ -69,8 +69,9 @@ export async function runMetadataTemplateUpsert(opts, deps = {}) {
|
|
|
69
69
|
const UPSERT_HELP = `
|
|
70
70
|
Upserts the instance's metadata-field schema. POST is an upsert keyed by
|
|
71
71
|
instance that MERGES the incoming sections by section 'label' — a section whose
|
|
72
|
-
label matches an existing one is replaced
|
|
73
|
-
|
|
72
|
+
label matches an existing one is replaced whole (fields left out of it are
|
|
73
|
+
removed), other sections are preserved. It cannot remove a whole section (a
|
|
74
|
+
full-replace / UI operation).
|
|
74
75
|
--instance-id the instance whose schema to upsert (required)
|
|
75
76
|
--sections inline JSON array of sections, OR
|
|
76
77
|
--sections-file path to a JSON file with the sections array (exactly one)
|
|
@@ -205,7 +205,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
205
205
|
is_required: true,
|
|
206
206
|
is_filterable: true,
|
|
207
207
|
is_sortable: true,
|
|
208
|
-
is_pitch_master: false,
|
|
209
208
|
help_text: 'Marketed product name',
|
|
210
209
|
default: '',
|
|
211
210
|
},
|
|
@@ -216,7 +215,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
216
215
|
is_required: false,
|
|
217
216
|
is_filterable: true,
|
|
218
217
|
is_sortable: true,
|
|
219
|
-
is_pitch_master: false,
|
|
220
218
|
},
|
|
221
219
|
{
|
|
222
220
|
name: 'therapeutic_area',
|
|
@@ -225,7 +223,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
225
223
|
is_required: false,
|
|
226
224
|
is_filterable: true,
|
|
227
225
|
is_sortable: false,
|
|
228
|
-
is_pitch_master: false,
|
|
229
226
|
options: [
|
|
230
227
|
{ label: 'Cardiology', value: 'cardiology' },
|
|
231
228
|
{ label: 'Oncology', value: 'oncology' },
|
|
@@ -239,7 +236,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
239
236
|
is_required: false,
|
|
240
237
|
is_filterable: true,
|
|
241
238
|
is_sortable: false,
|
|
242
|
-
is_pitch_master: false,
|
|
243
239
|
options: [
|
|
244
240
|
{ label: 'HCP', value: 'hcp' },
|
|
245
241
|
{ label: 'Payer', value: 'payer' },
|
|
@@ -253,7 +249,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
253
249
|
is_required: false,
|
|
254
250
|
is_filterable: false,
|
|
255
251
|
is_sortable: true,
|
|
256
|
-
is_pitch_master: false,
|
|
257
252
|
default: 0,
|
|
258
253
|
},
|
|
259
254
|
{
|
|
@@ -263,7 +258,6 @@ const METADATA_TEMPLATE_SCAFFOLD_DATA = [
|
|
|
263
258
|
is_required: false,
|
|
264
259
|
is_filterable: true,
|
|
265
260
|
is_sortable: false,
|
|
266
|
-
is_pitch_master: false,
|
|
267
261
|
default: false,
|
|
268
262
|
},
|
|
269
263
|
],
|
|
@@ -65,7 +65,11 @@ export declare function buildAuthorizeUrl(p: {
|
|
|
65
65
|
state: string;
|
|
66
66
|
codeChallenge: string;
|
|
67
67
|
}): string;
|
|
68
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* null = the domain is not a Pitcher org (404, or no auth0 block). Throws an UpstreamError on a network error or
|
|
70
|
+
* any other status: a 400 or a 429 says nothing about the org, and the hosted server reads null as "this token's
|
|
71
|
+
* org is gone" (401, sign in again) where an upstream error is a retryable 503.
|
|
72
|
+
*/
|
|
69
73
|
export declare function fetchOrgConfig(domain: string, opts?: HttpOpts): Promise<OrgConfig | null>;
|
|
70
74
|
export declare function exchangeCode(p: {
|
|
71
75
|
auth0Domain: string;
|
|
@@ -35,6 +35,8 @@ export function orgSubdomain(domain) {
|
|
|
35
35
|
export const AUTH0_NATIVE_CLIENT_IDS = {
|
|
36
36
|
'auth0.test-eu.pitcher.com': 'EvXiukSFA9LmxkoMyGfGrX6tk5eZdJFu',
|
|
37
37
|
'auth0.eu.pitcher.com': 'cJpAajaha7dWc2mnGy3GZSMBm3kzX370',
|
|
38
|
+
'auth0.test-us.pitcher.com': 'sd6biLE9qGBwUfSzl2uRC1p5JdhKY06N',
|
|
39
|
+
'auth0.us.pitcher.com': 'k6BgMI2Oy26RnyucUsWiL81JOeaufECm',
|
|
38
40
|
};
|
|
39
41
|
export function createUpstreamError(kind, message) {
|
|
40
42
|
return Object.assign(new Error(message), { isUpstreamError: true, kind });
|
|
@@ -60,10 +62,16 @@ export function buildAuthorizeUrl(p) {
|
|
|
60
62
|
}).toString();
|
|
61
63
|
return u.href;
|
|
62
64
|
}
|
|
63
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* null = the domain is not a Pitcher org (404, or no auth0 block). Throws an UpstreamError on a network error or
|
|
67
|
+
* any other status: a 400 or a 429 says nothing about the org, and the hosted server reads null as "this token's
|
|
68
|
+
* org is gone" (401, sign in again) where an upstream error is a retryable 503.
|
|
69
|
+
*/
|
|
64
70
|
export async function fetchOrgConfig(domain, opts = {}) {
|
|
65
71
|
const doFetch = opts.fetch ?? globalThis.fetch;
|
|
66
|
-
|
|
72
|
+
// No `org_subdomain` param: the edge sets `x-original-domain` from the host, which names the same org, and
|
|
73
|
+
// next-core reports every request carrying the param to Sentry as deprecated.
|
|
74
|
+
const url = `https://${domain}/core/api/org-config`;
|
|
67
75
|
let res;
|
|
68
76
|
try {
|
|
69
77
|
res = await doFetch(url, {
|
|
@@ -76,10 +84,10 @@ export async function fetchOrgConfig(domain, opts = {}) {
|
|
|
76
84
|
catch {
|
|
77
85
|
throw createUpstreamError('upstream', 'org-config unreachable');
|
|
78
86
|
}
|
|
79
|
-
if (res.status
|
|
80
|
-
throw createUpstreamError('upstream', `org-config returned ${res.status}`);
|
|
81
|
-
if (res.status !== 200)
|
|
87
|
+
if (res.status === 404)
|
|
82
88
|
return null;
|
|
89
|
+
if (res.status !== 200)
|
|
90
|
+
throw createUpstreamError('upstream', `org-config returned ${res.status}`);
|
|
83
91
|
const json = await res.json().catch(() => null);
|
|
84
92
|
const a = json?.auth0;
|
|
85
93
|
if (!a || typeof a.domain !== 'string' || typeof a.audience !== 'string')
|
package/dist/src/mcp/hosted.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PromptDescriptor } from './prompts/types.js';
|
|
1
2
|
import type { BuildServerOptions } from './server.js';
|
|
2
3
|
import type { ToolDescriptor } from './tools/types.js';
|
|
3
4
|
/**
|
|
@@ -16,6 +17,20 @@ export declare const LOCAL_ONLY: ReadonlySet<string>;
|
|
|
16
17
|
* exposes today (`questionFile`, `answersFile`, `canvasFile`, `assessmentsFile`, `contentHtmlFile`).
|
|
17
18
|
*/
|
|
18
19
|
export declare const FILE_KEY: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* An id-shaped input (`id`, `instanceId`, `courseIds`, …). Many commands interpolate ids straight into the
|
|
22
|
+
* request PATH (`/api/v1/files/${id}`, the L&D `/admin/courses/${id}`), so a value like `../users/1` or `1?x=`
|
|
23
|
+
* would retarget the call to another endpoint on the same host with the caller's token. Hosted accepts only
|
|
24
|
+
* plain tokens (letters, digits, `_`, `-`; comma-separated lists for string list inputs).
|
|
25
|
+
*/
|
|
26
|
+
export declare const ID_KEY: RegExp;
|
|
19
27
|
/** Build options every hosted server uses: a write always asks the client to confirm. */
|
|
20
28
|
export declare const HOSTED_BUILD_OPTIONS: Readonly<BuildServerOptions>;
|
|
21
29
|
export declare const hostedTools: ToolDescriptor[];
|
|
30
|
+
/**
|
|
31
|
+
* Read-only mode (the org's `settings.mcp.access` is `read_only`, PIT-9768): only the hosted tools that read.
|
|
32
|
+
* A write tool is never registered, so calling one is "unknown tool", not a refused write.
|
|
33
|
+
*/
|
|
34
|
+
export declare const hostedReadOnlyTools: ToolDescriptor[];
|
|
35
|
+
/** The prompts a read-only server serves: only those flagged `readOnly` (the rest drive a write tool). */
|
|
36
|
+
export declare const readOnlyPrompts: PromptDescriptor[];
|
package/dist/src/mcp/hosted.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fail } from '../core/envelope.js';
|
|
2
2
|
import { createCliError } from '../core/errors.js';
|
|
3
|
+
import { allPrompts } from './prompts/registry.js';
|
|
3
4
|
import { allTools } from './registry.js';
|
|
4
5
|
/**
|
|
5
6
|
* The tool set served by the hosted (remote, OAuth) MCP server — PIT-8912.
|
|
@@ -21,6 +22,26 @@ export const LOCAL_ONLY = new Set([
|
|
|
21
22
|
* exposes today (`questionFile`, `answersFile`, `canvasFile`, `assessmentsFile`, `contentHtmlFile`).
|
|
22
23
|
*/
|
|
23
24
|
export const FILE_KEY = /File$|^filePath$/;
|
|
25
|
+
/**
|
|
26
|
+
* An id-shaped input (`id`, `instanceId`, `courseIds`, …). Many commands interpolate ids straight into the
|
|
27
|
+
* request PATH (`/api/v1/files/${id}`, the L&D `/admin/courses/${id}`), so a value like `../users/1` or `1?x=`
|
|
28
|
+
* would retarget the call to another endpoint on the same host with the caller's token. Hosted accepts only
|
|
29
|
+
* plain tokens (letters, digits, `_`, `-`; comma-separated lists for string list inputs).
|
|
30
|
+
*/
|
|
31
|
+
export const ID_KEY = /^(id|ids|.+Id|.+Ids)$/;
|
|
32
|
+
const ID_TOKEN = /^[A-Za-z0-9_-]{1,200}$/;
|
|
33
|
+
function isSafeIdValue(v) {
|
|
34
|
+
// '' is "absent" to every command (falsy) and reaches no path
|
|
35
|
+
if (v === undefined || v === null || v === '' || typeof v === 'boolean')
|
|
36
|
+
return true;
|
|
37
|
+
if (typeof v === 'number')
|
|
38
|
+
return Number.isSafeInteger(v);
|
|
39
|
+
if (typeof v === 'string')
|
|
40
|
+
return v.split(',').every((part) => ID_TOKEN.test(part.trim()));
|
|
41
|
+
if (Array.isArray(v))
|
|
42
|
+
return v.every((x) => (typeof x === 'number' ? Number.isSafeInteger(x) : ID_TOKEN.test(String(x))));
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
24
45
|
/** Build options every hosted server uses: a write always asks the client to confirm. */
|
|
25
46
|
export const HOSTED_BUILD_OPTIONS = Object.freeze({ destructiveWrites: true });
|
|
26
47
|
// Tool descriptions rewritten without the local-file wording (the stdio text is kept for p mcp).
|
|
@@ -29,7 +50,7 @@ const HOSTED_DESCRIPTIONS = {
|
|
|
29
50
|
pitcher_files_update: 'Update a file by ID (name, folder, access, tags, metadata). sourceUrl replaces/overwrites the file binary; metadata is deep-merged.',
|
|
30
51
|
pitcher_announcements_create: 'Create a wa-announcements appsDB record (title/body, optional media, CTA link, scheduling, group targeting). Requires title and body. Additive.',
|
|
31
52
|
pitcher_dsr_create: 'Create a Digital Sales Room (a canvas share link) and get a clickable link back. Source is a canvas (canvasId) OR files (shareboxFileIds) - exactly one. Optional email/domain gating and a bound landing-page template. Returns the share: id, url (the full link), short_url, and a nested canvas object - there is no full_url field. This endpoint is a GET-OR-CREATE: sharing an already-shared canvas returns the existing link and only updates the fields you pass, so use pitcher_dsr_update to change or remove a value on an existing share. A failed landing bind does not fail the call: it returns the link with a warning - and binding a landing page needs instanceId, without which the bind is skipped with that warning. tags are a ShareBox-only field (they name the canvas the server builds from shareboxFileIds), so non-empty tags together with canvasId are REJECTED rather than silently dropped by the server.',
|
|
32
|
-
pitcher_metadata_template_upsert: "Upsert an instance's metadata-field schema. Merges the incoming sections by section 'label' — a section whose label matches an existing one is
|
|
53
|
+
pitcher_metadata_template_upsert: "Upsert an instance's metadata-field schema. Merges the incoming sections by section 'label' — a section whose label matches an existing one is REPLACED WHOLE (fields left out of it are removed), other sections are preserved. Cannot remove a whole section. To add a field, send the section with all its existing fields plus the new one. Requires an instance- or org-admin role.",
|
|
33
54
|
};
|
|
34
55
|
// Field descriptions that name a stripped key, rewritten for hosted (`tool name → field → text`).
|
|
35
56
|
const HOSTED_FIELD_DESCRIPTIONS = {
|
|
@@ -67,16 +88,28 @@ function toHosted(t) {
|
|
|
67
88
|
...t,
|
|
68
89
|
description: HOSTED_DESCRIPTIONS[t.name] ?? t.description,
|
|
69
90
|
inputShape: hostedShape(t),
|
|
70
|
-
// Runtime
|
|
91
|
+
// Runtime guards, independent of the schema: the stripped keys are invisible to a well-behaved
|
|
71
92
|
// client, but this refuses any file-path key however it arrives (a schema that grows a new
|
|
72
|
-
// xFile key, a caller bypassing validation, a command-layer key no schema declares)
|
|
93
|
+
// xFile key, a caller bypassing validation, a command-layer key no schema declares), and any
|
|
94
|
+
// id that is not a plain token (ID_KEY).
|
|
73
95
|
run: (opts, deps) => {
|
|
74
96
|
const bad = Object.keys(opts).filter((k) => FILE_KEY.test(k) && opts[k] !== undefined);
|
|
75
97
|
if (bad.length) {
|
|
76
98
|
return Promise.resolve(fail(createCliError('VALIDATION', `not supported on the hosted server (no local files): ${bad.join(', ')}`)));
|
|
77
99
|
}
|
|
100
|
+
const badIds = Object.keys(opts).filter((k) => ID_KEY.test(k) && !isSafeIdValue(opts[k]));
|
|
101
|
+
if (badIds.length) {
|
|
102
|
+
return Promise.resolve(fail(createCliError('VALIDATION', `not a valid id (letters, digits, "_" and "-" only): ${badIds.join(', ')}`)));
|
|
103
|
+
}
|
|
78
104
|
return t.run(opts, deps);
|
|
79
105
|
},
|
|
80
106
|
};
|
|
81
107
|
}
|
|
82
108
|
export const hostedTools = allTools.filter((t) => !LOCAL_ONLY.has(t.name)).map(toHosted);
|
|
109
|
+
/**
|
|
110
|
+
* Read-only mode (the org's `settings.mcp.access` is `read_only`, PIT-9768): only the hosted tools that read.
|
|
111
|
+
* A write tool is never registered, so calling one is "unknown tool", not a refused write.
|
|
112
|
+
*/
|
|
113
|
+
export const hostedReadOnlyTools = hostedTools.filter((t) => t.readOnly && !t.write && !t.alwaysDestructive);
|
|
114
|
+
/** The prompts a read-only server serves: only those flagged `readOnly` (the rest drive a write tool). */
|
|
115
|
+
export const readOnlyPrompts = allPrompts.filter((p) => p.readOnly === true);
|
package/dist/src/mcp/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { buildServer, type McpRunContext, type BuildServerOptions } from './server.js';
|
|
2
|
-
export { hostedTools, LOCAL_ONLY, FILE_KEY, HOSTED_BUILD_OPTIONS } from './hosted.js';
|
|
1
|
+
export { buildServer, PITCHER_ICON, type McpRunContext, type BuildServerOptions } from './server.js';
|
|
2
|
+
export { hostedTools, hostedReadOnlyTools, readOnlyPrompts, LOCAL_ONLY, FILE_KEY, ID_KEY, HOSTED_BUILD_OPTIONS, } from './hosted.js';
|
|
3
3
|
export { allTools } from './registry.js';
|
|
4
4
|
export { allPrompts } from './prompts/registry.js';
|
|
5
5
|
export { allResources } from './resources/registry.js';
|
package/dist/src/mcp/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Public entry `@pitcher/cli-scripts/mcp` — what the hosted MCP server (web/packages/mcp-server) consumes.
|
|
2
|
-
export { buildServer } from './server.js';
|
|
3
|
-
export { hostedTools, LOCAL_ONLY, FILE_KEY, HOSTED_BUILD_OPTIONS } from './hosted.js';
|
|
2
|
+
export { buildServer, PITCHER_ICON } from './server.js';
|
|
3
|
+
export { hostedTools, hostedReadOnlyTools, readOnlyPrompts, LOCAL_ONLY, FILE_KEY, ID_KEY, HOSTED_BUILD_OPTIONS, } from './hosted.js';
|
|
4
4
|
export { allTools } from './registry.js';
|
|
5
5
|
export { allPrompts } from './prompts/registry.js';
|
|
6
6
|
export { allResources } from './resources/registry.js';
|
package/dist/src/mcp/local.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The tool set of LOCAL `p mcp` (PIT-8943): only the three disk-bound tools (`dev_sync`, `dev_sync_revert`,
|
|
3
3
|
* `publish_app`), which zip a folder on this machine and so cannot run hosted. Everything else is the hosted
|
|
4
|
-
* server (`https://
|
|
4
|
+
* server (`https://mcp.pitcher.com/mcp`).
|
|
5
5
|
*/
|
|
6
6
|
export declare const localTools: import("./index.js").ToolDescriptor[];
|
package/dist/src/mcp/local.js
CHANGED
|
@@ -3,6 +3,6 @@ import { allTools } from './registry.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* The tool set of LOCAL `p mcp` (PIT-8943): only the three disk-bound tools (`dev_sync`, `dev_sync_revert`,
|
|
5
5
|
* `publish_app`), which zip a folder on this machine and so cannot run hosted. Everything else is the hosted
|
|
6
|
-
* server (`https://
|
|
6
|
+
* server (`https://mcp.pitcher.com/mcp`).
|
|
7
7
|
*/
|
|
8
8
|
export const localTools = allTools.filter((t) => LOCAL_ONLY.has(t.name));
|
|
@@ -7,6 +7,7 @@ import { instanceLine, readSafetyNote } from './shared.js';
|
|
|
7
7
|
export const analyticsPrompts = [
|
|
8
8
|
{
|
|
9
9
|
name: 'top_content',
|
|
10
|
+
readOnly: true,
|
|
10
11
|
title: 'Top-performing content',
|
|
11
12
|
description: 'Rank an instance’s most-viewed canvases (and optionally files) over a window.',
|
|
12
13
|
argsSchema: {
|
|
@@ -34,6 +35,7 @@ export const analyticsPrompts = [
|
|
|
34
35
|
},
|
|
35
36
|
{
|
|
36
37
|
name: 'content_engagement_summary',
|
|
38
|
+
readOnly: true,
|
|
37
39
|
title: 'Content engagement summary',
|
|
38
40
|
description: 'Summarize per-content/DSR engagement for an instance over a window.',
|
|
39
41
|
argsSchema: {
|
|
@@ -59,6 +61,7 @@ export const analyticsPrompts = [
|
|
|
59
61
|
},
|
|
60
62
|
{
|
|
61
63
|
name: 'who_viewed_canvas',
|
|
64
|
+
readOnly: true,
|
|
62
65
|
title: 'Who viewed a DSR canvas',
|
|
63
66
|
description: 'List the viewers of a specific DSR canvas, then drill into one viewer’s session timeline.',
|
|
64
67
|
argsSchema: {
|
|
@@ -89,6 +92,7 @@ export const analyticsPrompts = [
|
|
|
89
92
|
},
|
|
90
93
|
{
|
|
91
94
|
name: 'adoption_snapshot',
|
|
95
|
+
readOnly: true,
|
|
92
96
|
title: 'Adoption snapshot',
|
|
93
97
|
description: 'Combine active-user stats and KPIs into a short adoption snapshot for an instance.',
|
|
94
98
|
argsSchema: {
|
package/dist/src/mcp/server.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ 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
|
+
export declare const PITCHER_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiI+PHJlY3Qgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiIHJ4PSIxMTYiIGZpbGw9IiMyOTMzNUMiLz48Y2lyY2xlIGN4PSIzNzIiIGN5PSIxNTAiIHI9IjMwIiBmaWxsPSIjQ0MwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTE1MCAxMTZoOTZhOTIgOTIgMCAwIDEgMCAxODRoLTUwdjk2aC00NnoiLz48cGF0aCBmaWxsPSIjMjkzMzVDIiBkPSJNMTk2IDE2MHY5Nmg1MGE0OCA0OCAwIDAgMCAwLTk2eiIvPjwvc3ZnPg==";
|
|
6
7
|
/**
|
|
7
8
|
* The per-server request context: who every tool call runs as, and against which origin.
|
|
8
9
|
* Captured at build time — local stdio `p mcp` builds one from the `p login` session; the hosted server
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -18,10 +18,10 @@ const version = (() => {
|
|
|
18
18
|
return '0.0.0';
|
|
19
19
|
}
|
|
20
20
|
})();
|
|
21
|
-
// Connector branding shown by MCP hosts (Claude Desktop, Cursor).
|
|
22
|
-
// data URI so it needs no network/asset resolution: a navy (#29335C) tile with a
|
|
21
|
+
// Connector branding shown by MCP hosts (Claude Desktop, Cursor) and on the hosted server's own pages.
|
|
22
|
+
// Self-contained data URI so it needs no network/asset resolution: a navy (#29335C) tile with a
|
|
23
23
|
// white "P" and the Pitcher-red (#CC0000) accent from the wordmark.
|
|
24
|
-
const
|
|
24
|
+
export const PITCHER_ICON = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiI+PHJlY3Qgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiIHJ4PSIxMTYiIGZpbGw9IiMyOTMzNUMiLz48Y2lyY2xlIGN4PSIzNzIiIGN5PSIxNTAiIHI9IjMwIiBmaWxsPSIjQ0MwMDAwIi8+PHBhdGggZmlsbD0iI2ZmZmZmZiIgZD0iTTE1MCAxMTZoOTZhOTIgOTIgMCAwIDEgMCAxODRoLTUwdjk2aC00NnoiLz48cGF0aCBmaWxsPSIjMjkzMzVDIiBkPSJNMTk2IDE2MHY5Nmg1MGE0OCA0OCAwIDAgMCAwLTk2eiIvPjwvc3ZnPg==';
|
|
25
25
|
// Compact, sanitized error string (no raw stack) — shared by the prompt/resource callbacks.
|
|
26
26
|
function errorText(e) {
|
|
27
27
|
if (isCliError(e))
|
|
@@ -35,7 +35,7 @@ export function buildServer(ctx, tools = allTools, prompts = allPrompts, resourc
|
|
|
35
35
|
title: 'Pitcher',
|
|
36
36
|
description: 'Search, manage, and share Pitcher content, canvases, files, DSRs, and instances.',
|
|
37
37
|
websiteUrl: 'https://www.pitcher.com',
|
|
38
|
-
icons: [{ src:
|
|
38
|
+
icons: [{ src: PITCHER_ICON, mimeType: 'image/svg+xml', sizes: ['any'] }],
|
|
39
39
|
},
|
|
40
40
|
// Surfaced to the host model via InitializeResult.instructions — the analytics answer-rendering
|
|
41
41
|
// policy so a natural-language question gets a right-sized reply (sentence / table / chart /
|
|
@@ -25,8 +25,9 @@ export const metadataTemplateTools = [
|
|
|
25
25
|
name: 'pitcher_metadata_template_upsert',
|
|
26
26
|
title: 'Upsert a metadata template',
|
|
27
27
|
description: "Upsert an instance's metadata-field schema. Merges the incoming sections by section 'label' — a " +
|
|
28
|
-
'section whose label matches an existing one is
|
|
29
|
-
'a section
|
|
28
|
+
'section whose label matches an existing one is REPLACED WHOLE (fields left out of it are removed), other ' +
|
|
29
|
+
'sections are preserved. Cannot remove a whole section. Pass exactly one of sections or sectionsFile. ' +
|
|
30
|
+
'Requires an instance- or org-admin key.',
|
|
30
31
|
inputShape: {
|
|
31
32
|
instanceId: z.string().describe('Instance ID (required)'),
|
|
32
33
|
sections: z.string().optional().describe('Sections JSON array (inline)'),
|
|
@@ -6,7 +6,7 @@ export interface ToolDescriptor {
|
|
|
6
6
|
name: string;
|
|
7
7
|
title: string;
|
|
8
8
|
description: string;
|
|
9
|
-
inputShape: z.
|
|
9
|
+
inputShape: Record<string, z.ZodType>;
|
|
10
10
|
run: (opts: Record<string, unknown> & InjectedAuth, deps?: any) => Promise<Envelope<unknown>>;
|
|
11
11
|
wraps?: (...a: any[]) => unknown;
|
|
12
12
|
readOnly: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pitcher/cli-scripts",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "The Pitcher CLI for various tasks in its ecosystem.",
|
|
5
5
|
"author": "Pitcher AG",
|
|
6
6
|
"license": "MIT",
|
|
@@ -57,6 +57,6 @@
|
|
|
57
57
|
"chokidar": "^5.0.0",
|
|
58
58
|
"commander": "^15.0.0",
|
|
59
59
|
"form-data": "^4.0.6",
|
|
60
|
-
"zod": "^
|
|
60
|
+
"zod": "^4.6.7"
|
|
61
61
|
}
|
|
62
62
|
}
|