@pitcher/cli-scripts 2.17.1 → 2.19.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 +69 -10
- package/dist/index.js +0 -0
- package/dist/src/commands/devSync.d.ts +2 -0
- package/dist/src/commands/devSync.js +5 -5
- package/dist/src/commands/dsr.d.ts +5 -0
- package/dist/src/commands/dsr.js +177 -21
- package/dist/src/commands/publishApp.d.ts +2 -0
- package/dist/src/commands/publishApp.js +3 -3
- package/dist/src/commands/scaffolds.js +11 -3
- package/dist/src/core/archive.js +3 -3
- package/dist/src/core/credential.d.ts +25 -0
- package/dist/src/core/credential.js +25 -0
- package/dist/src/core/http.d.ts +8 -2
- package/dist/src/core/http.js +168 -8
- package/dist/src/core/ld.d.ts +38 -2
- package/dist/src/core/ld.js +162 -6
- package/dist/src/core/readInput.js +8 -1
- package/dist/src/mcp/bundled.js +1 -1
- package/dist/src/mcp/hosted.d.ts +21 -0
- package/dist/src/mcp/hosted.js +82 -0
- package/dist/src/mcp/index.d.ts +7 -0
- package/dist/src/mcp/index.js +6 -0
- package/dist/src/mcp/prompts/dsr.js +4 -0
- package/dist/src/mcp/serve.js +1 -1
- package/dist/src/mcp/server.d.ts +19 -2
- package/dist/src/mcp/server.js +4 -4
- package/dist/src/mcp/tools/devSync.js +2 -2
- package/dist/src/mcp/tools/dsr.js +37 -8
- package/dist/src/mcp/tools/publishApp.js +2 -1
- package/dist/src/mcp/tools/types.d.ts +1 -1
- package/package.json +17 -6
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { fail } from '../core/envelope.js';
|
|
2
|
+
import { createCliError } from '../core/errors.js';
|
|
3
|
+
import { allTools } from './registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* The tool set served by the hosted (remote, OAuth) MCP server — PIT-8912.
|
|
6
|
+
*
|
|
7
|
+
* Differences from stdio `p mcp`:
|
|
8
|
+
* - the three disk-bound tools (`LOCAL_ONLY`) are dropped: they zip a folder on the server host;
|
|
9
|
+
* - every file-path input (`FILE_KEY`) is stripped from the schemas AND rejected at runtime, so a
|
|
10
|
+
* hosted call can never make the server read its own disk; every one has an inline alternative;
|
|
11
|
+
* - descriptions that talk about local files are rewritten.
|
|
12
|
+
*/
|
|
13
|
+
export const LOCAL_ONLY = new Set([
|
|
14
|
+
'pitcher_dev_sync',
|
|
15
|
+
'pitcher_dev_sync_revert',
|
|
16
|
+
'pitcher_publish_app',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* A file-path input. Covers the 8 schema keys on main (`filePath`, `configFile`, `landingContextFile`,
|
|
20
|
+
* `htmlFile`, `cssFile`, `bodyFile`, `fromFile`, `sectionsFile`) AND command-layer keys no schema
|
|
21
|
+
* exposes today (`questionFile`, `answersFile`, `canvasFile`, `assessmentsFile`, `contentHtmlFile`).
|
|
22
|
+
*/
|
|
23
|
+
export const FILE_KEY = /File$|^filePath$/;
|
|
24
|
+
/** Build options every hosted server uses: a write always asks the client to confirm. */
|
|
25
|
+
export const HOSTED_BUILD_OPTIONS = Object.freeze({ destructiveWrites: true });
|
|
26
|
+
// Tool descriptions rewritten without the local-file wording (the stdio text is kept for p mcp).
|
|
27
|
+
const HOSTED_DESCRIPTIONS = {
|
|
28
|
+
pitcher_files_create: 'Create a content file in an instance from a source URL. Additive.',
|
|
29
|
+
pitcher_files_update: 'Update a file by ID (name, folder, access, tags, metadata). sourceUrl replaces/overwrites the file binary; metadata is deep-merged.',
|
|
30
|
+
pitcher_announcements_create: 'Create a wa-announcements appsDB record (title/body, optional media, CTA link, scheduling, group targeting). Requires title and body. Additive.',
|
|
31
|
+
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 replaced, other sections are preserved. Cannot remove a section or field. Requires an instance- or org-admin role.",
|
|
33
|
+
};
|
|
34
|
+
// Field descriptions that name a stripped key, rewritten for hosted (`tool name → field → text`).
|
|
35
|
+
const HOSTED_FIELD_DESCRIPTIONS = {
|
|
36
|
+
pitcher_files_update: {
|
|
37
|
+
instanceId: 'Instance ID (optional; not needed to update from a sourceUrl)',
|
|
38
|
+
},
|
|
39
|
+
pitcher_announcements_create: {
|
|
40
|
+
title: 'Announcement title',
|
|
41
|
+
body: 'Announcement body HTML',
|
|
42
|
+
priority: "Priority, e.g. 'normal' or 'important'",
|
|
43
|
+
},
|
|
44
|
+
pitcher_dsr_landing_create: {
|
|
45
|
+
html: 'Landing HTML (must contain data-pitcher-email-gate)',
|
|
46
|
+
css: 'CSS for the template',
|
|
47
|
+
},
|
|
48
|
+
pitcher_dsr_create: {
|
|
49
|
+
ifExists: 'Canvas already shared: "return" (default) hands back the existing link, "error" refuses and points at pitcher_dsr_update. Not checked for a ShareBox source, which always mints a new canvas',
|
|
50
|
+
},
|
|
51
|
+
pitcher_email_template_create: {
|
|
52
|
+
bodyHtml: 'HTML body (inline CSS).',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
function hostedShape(t) {
|
|
56
|
+
const fields = HOSTED_FIELD_DESCRIPTIONS[t.name] ?? {};
|
|
57
|
+
const shape = {};
|
|
58
|
+
for (const [key, schema] of Object.entries(t.inputShape)) {
|
|
59
|
+
if (FILE_KEY.test(key))
|
|
60
|
+
continue;
|
|
61
|
+
shape[key] = fields[key] !== undefined ? schema.describe(fields[key]) : schema;
|
|
62
|
+
}
|
|
63
|
+
return shape;
|
|
64
|
+
}
|
|
65
|
+
function toHosted(t) {
|
|
66
|
+
return {
|
|
67
|
+
...t,
|
|
68
|
+
description: HOSTED_DESCRIPTIONS[t.name] ?? t.description,
|
|
69
|
+
inputShape: hostedShape(t),
|
|
70
|
+
// Runtime guard, independent of the schema: the stripped keys are invisible to a well-behaved
|
|
71
|
+
// 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).
|
|
73
|
+
run: (opts, deps) => {
|
|
74
|
+
const bad = Object.keys(opts).filter((k) => FILE_KEY.test(k) && opts[k] !== undefined);
|
|
75
|
+
if (bad.length) {
|
|
76
|
+
return Promise.resolve(fail(createCliError('VALIDATION', `not supported on the hosted server (no local files): ${bad.join(', ')}`)));
|
|
77
|
+
}
|
|
78
|
+
return t.run(opts, deps);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export const hostedTools = allTools.filter((t) => !LOCAL_ONLY.has(t.name)).map(toHosted);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { buildServer, type McpRunContext, type BuildServerOptions } from './server.js';
|
|
2
|
+
export { hostedTools, LOCAL_ONLY, FILE_KEY, HOSTED_BUILD_OPTIONS } from './hosted.js';
|
|
3
|
+
export { allTools } from './registry.js';
|
|
4
|
+
export { allPrompts } from './prompts/registry.js';
|
|
5
|
+
export { allResources } from './resources/registry.js';
|
|
6
|
+
export type { ToolDescriptor } from './tools/types.js';
|
|
7
|
+
export type { Credential } from '../core/credential.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
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';
|
|
4
|
+
export { allTools } from './registry.js';
|
|
5
|
+
export { allPrompts } from './prompts/registry.js';
|
|
6
|
+
export { allResources } from './resources/registry.js';
|
|
@@ -20,6 +20,8 @@ export const dsrPrompts = [
|
|
|
20
20
|
'',
|
|
21
21
|
'## DSR sharing guidance',
|
|
22
22
|
'- **Source is exactly one of** a canvas (`canvasId`) OR files (`shareboxFileIds`).',
|
|
23
|
+
'- **`tags` are ShareBox-only.** They name the canvas the server builds from `shareboxFileIds`, so non-empty ' +
|
|
24
|
+
'`tags` together with `canvasId` is rejected rather than silently ignored.',
|
|
23
25
|
`- **Pick the gating (\`authStrategy\`) by audience.** For ${audience}: internal/quick demo → \`none\` ` +
|
|
24
26
|
'(open link); a known individual → `specified_emails` (+ `allowedEmails`); a company → ' +
|
|
25
27
|
'`specified_domains` (+ `allowedDomains`); the server default is `any_email`. Add `validUntil` ' +
|
|
@@ -27,6 +29,8 @@ export const dsrPrompts = [
|
|
|
27
29
|
'- **Branding the pre-auth page is a SEPARATE step:** this recipe only **binds** an existing ' +
|
|
28
30
|
'landing template (`landingTemplateId`, discover via `pitcher_dsr_landing_list`; pass ' +
|
|
29
31
|
'`landingContext` merge fields **as a JSON object serialized to a string**, not as an object). ' +
|
|
32
|
+
'**A landing bind needs `instanceId`** — the endpoint resolves the instance from it, so without it the bind ' +
|
|
33
|
+
'is skipped and the share comes back with a warning. ' +
|
|
30
34
|
'If no suitable template exists and the user wants a ' +
|
|
31
35
|
'branded landing, author one FIRST with the **`seed_dsr_landing`** recipe, then bind its id here. ' +
|
|
32
36
|
'Do NOT hand-write landing HTML in this recipe.',
|
package/dist/src/mcp/serve.js
CHANGED
|
@@ -9,7 +9,7 @@ import { allResources } from './resources/registry.js';
|
|
|
9
9
|
// Prompts/resources default to the full set for every frontend (no subsetting).
|
|
10
10
|
export async function runMcpServe(env = process.env, tools = allTools, prompts = allPrompts, resources = allResources) {
|
|
11
11
|
const mcpEnv = resolveMcpEnv(env);
|
|
12
|
-
const server = buildServer(mcpEnv, tools, prompts, resources);
|
|
12
|
+
const server = buildServer({ credential: { kind: 'apiKey', key: mcpEnv.apiKey }, org: mcpEnv.org, url: mcpEnv.url }, tools, prompts, resources, { destructiveWrites: mcpEnv.prod });
|
|
13
13
|
const transport = new StdioServerTransport();
|
|
14
14
|
await server.connect(transport);
|
|
15
15
|
const target = mcpEnv.url ?? `https://${mcpEnv.org}.my.pitcher.com`;
|
package/dist/src/mcp/server.d.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type {
|
|
2
|
+
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
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The per-server request context: who every tool call runs as, and against which origin.
|
|
8
|
+
* A plain value captured at build time — stdio builds one from env (API key); the hosted server
|
|
9
|
+
* builds one server per request from the caller's bearer token. Never a tool input.
|
|
10
|
+
*/
|
|
11
|
+
export interface McpRunContext {
|
|
12
|
+
credential: Credential;
|
|
13
|
+
org?: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface BuildServerOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Mark every `write` tool `destructiveHint: true`. Annotations are fixed at registration, so this
|
|
19
|
+
* is a build option, never a per-call field. Stdio passes `env.prod`; hosted always passes true.
|
|
20
|
+
*/
|
|
21
|
+
destructiveWrites?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function buildServer(ctx: McpRunContext, tools?: ToolDescriptor[], prompts?: PromptDescriptor[], resources?: ResourceDescriptor[], opts?: BuildServerOptions): McpServer;
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -33,7 +33,7 @@ function errorText(e) {
|
|
|
33
33
|
return `${e.code}: ${e.message}`;
|
|
34
34
|
return e instanceof Error ? e.message : String(e);
|
|
35
35
|
}
|
|
36
|
-
export function buildServer(
|
|
36
|
+
export function buildServer(ctx, tools = allTools, prompts = allPrompts, resources = allResources, opts = {}) {
|
|
37
37
|
const server = new McpServer({
|
|
38
38
|
name: 'pitcher',
|
|
39
39
|
version,
|
|
@@ -47,7 +47,7 @@ export function buildServer(env, tools = allTools, prompts = allPrompts, resourc
|
|
|
47
47
|
// dashboard) instead of a rigid full dashboard every time. See renderPolicy.ts.
|
|
48
48
|
{ instructions: ANALYTICS_RENDER_POLICY });
|
|
49
49
|
for (const tool of tools) {
|
|
50
|
-
const destructive = tool.alwaysDestructive === true || (tool.write === true &&
|
|
50
|
+
const destructive = tool.alwaysDestructive === true || (tool.write === true && opts.destructiveWrites === true);
|
|
51
51
|
server.registerTool(tool.name, {
|
|
52
52
|
title: tool.title,
|
|
53
53
|
description: tool.description,
|
|
@@ -55,8 +55,8 @@ export function buildServer(env, tools = allTools, prompts = allPrompts, resourc
|
|
|
55
55
|
annotations: { readOnlyHint: tool.readOnly, destructiveHint: destructive },
|
|
56
56
|
}, async (args) => {
|
|
57
57
|
try {
|
|
58
|
-
// spread args FIRST so injected auth/env always wins — never reorder to {
|
|
59
|
-
const envelope = await tool.run({ ...args,
|
|
58
|
+
// spread args FIRST so injected auth/env always wins — never reorder to { credential, ...args }
|
|
59
|
+
const envelope = await tool.run({ ...args, credential: ctx.credential, org: ctx.org, url: ctx.url });
|
|
60
60
|
return envelopeToToolResult(envelope);
|
|
61
61
|
}
|
|
62
62
|
catch (e) {
|
|
@@ -27,7 +27,7 @@ export const devSyncTools = [
|
|
|
27
27
|
const fileId = requireFileId(opts);
|
|
28
28
|
if (!fileId)
|
|
29
29
|
return Promise.resolve(fail(createCliError('VALIDATION', 'fileId is required')));
|
|
30
|
-
return runDevSync({ fileId, apiKey: opts.apiKey, org: opts.org }, deps);
|
|
30
|
+
return runDevSync({ fileId, apiKey: opts.apiKey, credential: opts.credential, org: opts.org }, deps);
|
|
31
31
|
},
|
|
32
32
|
wraps: runDevSync,
|
|
33
33
|
readOnly: false,
|
|
@@ -44,7 +44,7 @@ export const devSyncTools = [
|
|
|
44
44
|
const fileId = requireFileId(opts);
|
|
45
45
|
if (!fileId)
|
|
46
46
|
return Promise.resolve(fail(createCliError('VALIDATION', 'fileId is required')));
|
|
47
|
-
return runRevert({ fileId, apiKey: opts.apiKey, org: opts.org }, deps);
|
|
47
|
+
return runRevert({ fileId, apiKey: opts.apiKey, credential: opts.credential, org: opts.org }, deps);
|
|
48
48
|
},
|
|
49
49
|
wraps: runRevert,
|
|
50
50
|
readOnly: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { runDsrCreate, runDsrBindLanding, runDsrUpdate, runDsrDelete, CLEARABLE_UPDATE_FIELDS, IF_EXISTS_MODES } from '../../commands/dsr.js';
|
|
2
|
+
import { runDsrCreate, runDsrGet, runDsrBindLanding, runDsrUpdate, runDsrDelete, CLEARABLE_UPDATE_FIELDS, IF_EXISTS_MODES } from '../../commands/dsr.js';
|
|
3
3
|
const AUTH_STRATEGIES = [
|
|
4
4
|
'none', 'any_email', 'specified_emails', 'specified_domains', 'verified_emails', 'verified_domains',
|
|
5
5
|
];
|
|
@@ -7,9 +7,9 @@ export const dsrTools = [
|
|
|
7
7
|
{
|
|
8
8
|
name: 'pitcher_dsr_create',
|
|
9
9
|
title: 'Create and share a DSR',
|
|
10
|
-
description: '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: with individual fields, 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 configFile spec sends its whole security block (nulls and empty lists included), so for an already-shared canvas it refuses by default (ifExists "error"). A failed landing bind does not fail the call: it returns the link with a warning.',
|
|
10
|
+
description: '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: with individual fields, 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 configFile spec sends its whole security block (nulls and empty lists included), so for an already-shared canvas it refuses by default (ifExists "error"). 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.',
|
|
11
11
|
inputShape: {
|
|
12
|
-
instanceId: z.string().optional().describe('Instance ID (required for the shareboxFileIds source)'),
|
|
12
|
+
instanceId: z.string().optional().describe('Instance ID (required for the shareboxFileIds source, and to bind a landing page)'),
|
|
13
13
|
canvasId: z.string().optional().describe('Canvas to share (exactly one of canvasId / shareboxFileIds)'),
|
|
14
14
|
shareboxFileIds: z.array(z.string()).optional().describe('File IDs to build a share from (exactly one of canvasId / shareboxFileIds)'),
|
|
15
15
|
canvasName: z.string().optional().describe('Name for the ShareBox-built canvas'),
|
|
@@ -20,10 +20,23 @@ export const dsrTools = [
|
|
|
20
20
|
allowedDomains: z.array(z.string()).optional().describe('Required for specified_domains / verified_domains. An EMPTY array is rejected'),
|
|
21
21
|
validUntil: z.string().optional().describe('Expiry, ISO datetime'),
|
|
22
22
|
allowForward: z.boolean().optional(),
|
|
23
|
-
allowDownload: z
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
allowDownload: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Let recipients download the shared files. Create-time only (pitcher_dsr_update does not accept it). Sent to the server exactly as given for either source - unlike tags, the CLI applies no source-specific guard to it, so read the created share back if you need to confirm the value took'),
|
|
27
|
+
tags: z
|
|
28
|
+
.array(z.string())
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Tags for the ShareBox-built canvas. ShareBox source ONLY - a non-empty array together with canvasId is rejected, because the server reads tags only when it builds the canvas itself and would otherwise ignore them. An empty array is accepted (it asks for nothing)'),
|
|
31
|
+
landingTemplateId: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Bind a landing-page template (discover via pitcher_dsr_landing_list). Needs instanceId: without it the bind is skipped and the share comes back with a warning'),
|
|
26
35
|
landingContext: z.string().optional().describe('Landing-page merge fields, a JSON object as a string'),
|
|
36
|
+
landingContextFile: z
|
|
37
|
+
.string()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe('Path on the MCP server host to a JSON file holding the landing-page merge fields - the same field as landingContext, read off disk. Pass one or the other, never both (both is rejected as VALIDATION). Prefer landingContext inline: in a host whose sandbox is a separate filesystem from the MCP subprocess, a path written there is not visible here'),
|
|
27
40
|
configFile: z.string().optional().describe('Path on the MCP server host to a full {source,security,landing} spec JSON; mutually exclusive with the individual flags'),
|
|
28
41
|
ifExists: z
|
|
29
42
|
.enum(IF_EXISTS_MODES)
|
|
@@ -34,15 +47,31 @@ export const dsrTools = [
|
|
|
34
47
|
readOnly: false,
|
|
35
48
|
write: true,
|
|
36
49
|
},
|
|
50
|
+
{
|
|
51
|
+
name: 'pitcher_dsr_get',
|
|
52
|
+
title: 'Read a DSR share',
|
|
53
|
+
description: "Read an EXISTING DSR share back by its shared-link id. It returns the ACCESS CONFIG only: auth_strategy, allowed_emails / allowed_domains, valid_until, allow_forward (plus allow_forward_raw, the rep's own toggle before the admin setting is applied), the bound landing page (landing_page_template_id, landing_page_template_snapshot, landing_page_context) and created_at / modified_at. It does NOT return the canvas or the file - keep the id pitcher_dsr_create handed back for those. Use it to confirm what pitcher_dsr_create or pitcher_dsr_update actually stored, or to check a share before changing it. Read-only - it changes nothing. ANY id that matches no share comes back NOT_FOUND, malformed ids included: the endpoint does not validate the id's format.",
|
|
54
|
+
inputShape: {
|
|
55
|
+
id: z.string().describe('Shared-link ID'),
|
|
56
|
+
},
|
|
57
|
+
run: runDsrGet,
|
|
58
|
+
readOnly: true,
|
|
59
|
+
},
|
|
37
60
|
{
|
|
38
61
|
name: 'pitcher_dsr_bind_landing',
|
|
39
62
|
title: 'Bind a DSR landing page',
|
|
40
|
-
description: 'Attach, re-brand, or unbind a landing-page template on an existing DSR share. Use landingTemplateId "none" to unbind.
|
|
63
|
+
description: 'Attach, re-brand, or unbind a landing-page template on an existing DSR share. Use landingTemplateId "none" to unbind. instanceId is REQUIRED: the endpoint resolves the instance from that query param and refuses the call without it. An admin or editor key can bind any share in the org; a member key only shares that member created (hard-fails AUTH otherwise).',
|
|
41
64
|
inputShape: {
|
|
42
65
|
id: z.string().describe('Shared-link ID'),
|
|
43
66
|
landingTemplateId: z.string().describe('Landing template ID, or "none" to unbind'),
|
|
44
67
|
landingContext: z.string().optional().describe('Landing-page merge fields, a JSON object as a string'),
|
|
45
|
-
|
|
68
|
+
landingContextFile: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('Path on the MCP server host to a JSON file holding the merge fields - the same field as landingContext, read off disk. Pass one or the other, never both (both is rejected as VALIDATION). Prefer landingContext inline: in a host whose sandbox is a separate filesystem from the MCP subprocess, a path written there is not visible here'),
|
|
72
|
+
instanceId: z
|
|
73
|
+
.string()
|
|
74
|
+
.describe('Instance ID. The endpoint resolves the instance from this query param and rejects the call without it'),
|
|
46
75
|
},
|
|
47
76
|
run: runDsrBindLanding,
|
|
48
77
|
readOnly: false,
|
|
@@ -21,7 +21,7 @@ export const publishAppTools = [
|
|
|
21
21
|
installToEnvName: z.string().optional().describe('Environment name (e.g. "test") to install the app to after publishing'),
|
|
22
22
|
},
|
|
23
23
|
// runPublishApp overloads `url` as BOTH origin and publish endpoint; the server injects NG_URL into
|
|
24
|
-
// every tool, so forward only apiKey + org (never url) plus this tool's own inputs.
|
|
24
|
+
// every tool, so forward only apiKey + credential + org (never url) plus this tool's own inputs.
|
|
25
25
|
run: (opts) => runPublishApp({
|
|
26
26
|
folder: asString(opts.folder),
|
|
27
27
|
incrementVersion: asBool(opts.incrementVersion),
|
|
@@ -29,6 +29,7 @@ export const publishAppTools = [
|
|
|
29
29
|
installTo: asString(opts.installTo),
|
|
30
30
|
installToEnvName: asString(opts.installToEnvName),
|
|
31
31
|
apiKey: opts.apiKey,
|
|
32
|
+
credential: opts.credential,
|
|
32
33
|
org: opts.org,
|
|
33
34
|
}),
|
|
34
35
|
wraps: runPublishApp,
|
|
@@ -1,7 +1,7 @@
|
|
|
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
|
-
export type InjectedAuth = Pick<CommonOpts, 'apiKey' | 'org' | 'url'>;
|
|
4
|
+
export type InjectedAuth = Pick<CommonOpts, 'apiKey' | 'credential' | 'org' | 'url'>;
|
|
5
5
|
export interface ToolDescriptor {
|
|
6
6
|
name: string;
|
|
7
7
|
title: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pitcher/cli-scripts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "The Pitcher CLI for various tasks in its ecosystem.",
|
|
5
5
|
"author": "Pitcher AG",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"main": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./mcp": {
|
|
21
|
+
"types": "./dist/src/mcp/index.d.ts",
|
|
22
|
+
"import": "./dist/src/mcp/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
15
26
|
"scripts": {
|
|
16
27
|
"build": "tsc",
|
|
17
28
|
"type-check": "tsc --noEmit && tsc -p mcpb/tsconfig.json",
|
|
@@ -28,18 +39,18 @@
|
|
|
28
39
|
],
|
|
29
40
|
"devDependencies": {
|
|
30
41
|
"@anthropic-ai/mcpb": "^2.1.2",
|
|
31
|
-
"@types/archiver": "^
|
|
32
|
-
"@types/node": "^24.13.
|
|
42
|
+
"@types/archiver": "^8.0.0",
|
|
43
|
+
"@types/node": "^24.13.5",
|
|
33
44
|
"esbuild": "^0.28.1",
|
|
34
45
|
"typescript": "^6.0.3",
|
|
35
|
-
"vitest": "^
|
|
46
|
+
"vitest": "^5.0.1"
|
|
36
47
|
},
|
|
37
48
|
"dependencies": {
|
|
38
49
|
"@modelcontextprotocol/sdk": "^1",
|
|
39
|
-
"archiver": "^
|
|
50
|
+
"archiver": "^8.0.0",
|
|
40
51
|
"axios": "^1.18.1",
|
|
41
52
|
"chokidar": "^5.0.0",
|
|
42
|
-
"commander": "^
|
|
53
|
+
"commander": "^15.0.0",
|
|
43
54
|
"form-data": "^4.0.6",
|
|
44
55
|
"zod": "^3"
|
|
45
56
|
}
|