@salesforce/graphiti 10.15.1 → 10.16.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/CHANGELOG.md +6 -0
- package/dist/cli.js +21 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/mcp-mirror/commands.d.ts +16 -0
- package/dist/commands/mcp-mirror/commands.js +38 -0
- package/dist/commands/mcp-mirror/commands.js.map +1 -0
- package/dist/commands/mcp-mirror/run-mirror.d.ts +42 -0
- package/dist/commands/mcp-mirror/run-mirror.js +105 -0
- package/dist/commands/mcp-mirror/run-mirror.js.map +1 -0
- package/dist/lib/graphql-name.d.ts +2 -2
- package/dist/lib/graphql-name.js +2 -2
- package/dist/mcp/tools/sf-gql-aggregate.js +2 -55
- package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -1
- package/dist/mcp/tools/sf-gql-connect.js +2 -9
- package/dist/mcp/tools/sf-gql-connect.js.map +1 -1
- package/dist/mcp/tools/sf-gql-create.js +2 -15
- package/dist/mcp/tools/sf-gql-create.js.map +1 -1
- package/dist/mcp/tools/sf-gql-delete.js +2 -11
- package/dist/mcp/tools/sf-gql-delete.js.map +1 -1
- package/dist/mcp/tools/sf-gql-detail.js +2 -30
- package/dist/mcp/tools/sf-gql-detail.js.map +1 -1
- package/dist/mcp/tools/sf-gql-discover.js +2 -33
- package/dist/mcp/tools/sf-gql-discover.js.map +1 -1
- package/dist/mcp/tools/sf-gql-list.js +2 -32
- package/dist/mcp/tools/sf-gql-list.js.map +1 -1
- package/dist/mcp/tools/sf-gql-raw.js +2 -19
- package/dist/mcp/tools/sf-gql-raw.js.map +1 -1
- package/dist/mcp/tools/sf-gql-update.js +2 -15
- package/dist/mcp/tools/sf-gql-update.js.map +1 -1
- package/dist/{mcp/tools/shared/zod-schemas.js → schemas/fields.js} +2 -2
- package/dist/schemas/fields.js.map +1 -0
- package/dist/schemas/input-schemas.d.ts +318 -0
- package/dist/schemas/input-schemas.js +230 -0
- package/dist/schemas/input-schemas.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +23 -1
- package/src/commands/mcp-mirror/__tests__/commands.spec.ts +146 -0
- package/src/commands/mcp-mirror/__tests__/run-mirror.spec.ts +251 -0
- package/src/commands/mcp-mirror/commands.ts +92 -0
- package/src/commands/mcp-mirror/run-mirror.ts +149 -0
- package/src/lib/graphql-name.ts +2 -2
- package/src/mcp/tools/sf-gql-aggregate.ts +2 -68
- package/src/mcp/tools/sf-gql-connect.ts +2 -11
- package/src/mcp/tools/sf-gql-create.ts +2 -21
- package/src/mcp/tools/sf-gql-delete.ts +2 -15
- package/src/mcp/tools/sf-gql-detail.ts +2 -42
- package/src/mcp/tools/sf-gql-discover.ts +2 -36
- package/src/mcp/tools/sf-gql-list.ts +2 -41
- package/src/mcp/tools/sf-gql-raw.ts +2 -25
- package/src/mcp/tools/sf-gql-update.ts +2 -21
- package/src/{mcp/tools/shared/zod-schemas.ts → schemas/fields.ts} +1 -1
- package/src/schemas/input-schemas.ts +305 -0
- package/dist/mcp/tools/shared/zod-schemas.js.map +0 -1
- package/dist/{mcp/tools/shared/zod-schemas.d.ts → schemas/fields.d.ts} +2 -2
|
@@ -5,55 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import {
|
|
10
|
-
childRelationshipSchema,
|
|
11
|
-
graphqlName,
|
|
12
|
-
orderByObject,
|
|
13
|
-
orgAlias,
|
|
14
|
-
} from "./shared/zod-schemas.js";
|
|
15
8
|
import { buildDetail } from "../../intent/build-detail.js";
|
|
16
9
|
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
10
|
+
import { DETAIL_INPUT } from "../../schemas/input-schemas.js";
|
|
17
11
|
|
|
18
12
|
export interface SfGqlDetailToolOptions {
|
|
19
13
|
primeDeps?: PrimeDeps;
|
|
20
14
|
}
|
|
21
15
|
|
|
22
|
-
|
|
23
|
-
// arrays at runtime via `z.preprocess` (FR-6.2 compat shim for early MCP
|
|
24
|
-
// clients that learned the array shape). The transform collapses to the first
|
|
25
|
-
// element before validation, so downstream code sees a singleton.
|
|
26
|
-
const orderBySchema = z.preprocess((v) => (Array.isArray(v) ? v[0] : v), orderByObject);
|
|
27
|
-
|
|
28
|
-
// Note: filter/orderBy/scope/first are intentionally absent at the top level —
|
|
29
|
-
// `sf_gql_detail` is single-record-by-Id (FR-5.5). Filtering happens via the
|
|
30
|
-
// declared `$<idVariable>: ID!`; pagination doesn't apply.
|
|
31
|
-
//
|
|
32
|
-
// `.strict()` rejects unknown top-level keys at the MCP boundary so an LLM
|
|
33
|
-
// hallucinating sibling-tool params (`filter`, `scope`, `first`) gets a
|
|
34
|
-
// validation error to learn from rather than silent omission.
|
|
35
|
-
const inputSchema = z
|
|
36
|
-
.object({
|
|
37
|
-
org: orgAlias("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
38
|
-
object: graphqlName('SObject API name, e.g. "Account", "Case". Must be a valid GraphQL Name.'),
|
|
39
|
-
fields: z
|
|
40
|
-
.array(z.string())
|
|
41
|
-
.describe(
|
|
42
|
-
'Scalar field API names to select on the node. Dot-paths like "Owner.Name" are allowed.',
|
|
43
|
-
),
|
|
44
|
-
parentFields: z
|
|
45
|
-
.array(z.string())
|
|
46
|
-
.optional()
|
|
47
|
-
.describe('Dotted parent-relationship paths, e.g. "Account.Name".'),
|
|
48
|
-
childRelationships: z.array(childRelationshipSchema(orderBySchema)).optional(),
|
|
49
|
-
idVariable: graphqlName(
|
|
50
|
-
'Name (without leading "$") for the ID variable. Defaults to "id". Declared as <name>: ID! and bound via where { Id: { eq: $<name> } } per FR-5.5. Choose a name that does not appear as a $varName in any childRelationships filter/orderBy — collisions are rejected.',
|
|
51
|
-
).optional(),
|
|
52
|
-
operationName: graphqlName(
|
|
53
|
-
"Override the GraphQL operation name. Defaults to <Object>Detail.",
|
|
54
|
-
).optional(),
|
|
55
|
-
})
|
|
56
|
-
.strict();
|
|
16
|
+
const inputSchema = DETAIL_INPUT;
|
|
57
17
|
|
|
58
18
|
export function registerSfGqlDetailTool(
|
|
59
19
|
server: McpServer,
|
|
@@ -5,46 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
8
|
import { buildDiscover, type DiscoverDeps } from "../../intent/build-discover.js";
|
|
9
|
+
import { DISCOVER_INPUT } from "../../schemas/input-schemas.js";
|
|
10
10
|
|
|
11
11
|
export type SfGqlDiscoverToolOptions = DiscoverDeps;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
// (ObjectInfo cache), and GraphQL operation names. Bound at the MCP boundary
|
|
15
|
-
// to safe API-name charsets so downstream layers can trust them.
|
|
16
|
-
const ORG_ALIAS_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
17
|
-
const SOBJECT_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,79}$/;
|
|
18
|
-
// eslint-disable-next-line no-control-regex -- intentionally rejects control chars
|
|
19
|
-
const SEARCH_RE = /^[^\x00-\x1f\x7f]*$/;
|
|
20
|
-
|
|
21
|
-
const inputSchema = {
|
|
22
|
-
org: z
|
|
23
|
-
.string()
|
|
24
|
-
.regex(ORG_ALIAS_RE, "org must match /^[A-Za-z0-9_-]{1,80}$/")
|
|
25
|
-
.describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
26
|
-
mode: z
|
|
27
|
-
.enum(["list_objects", "describe_object", "describe_field"])
|
|
28
|
-
.describe(
|
|
29
|
-
'Discovery mode. "list_objects" enumerates queryable SObjects; "describe_object" and "describe_field" return ObjectInfo metadata.',
|
|
30
|
-
),
|
|
31
|
-
object: z
|
|
32
|
-
.string()
|
|
33
|
-
.regex(SOBJECT_NAME_RE, "object must match /^[A-Za-z][A-Za-z0-9_]{0,79}$/")
|
|
34
|
-
.optional()
|
|
35
|
-
.describe('SObject API name. Required for "describe_object" and "describe_field".'),
|
|
36
|
-
field: z
|
|
37
|
-
.string()
|
|
38
|
-
.regex(SOBJECT_NAME_RE, "field must match /^[A-Za-z][A-Za-z0-9_]{0,79}$/")
|
|
39
|
-
.optional()
|
|
40
|
-
.describe('Field API name. Required for "describe_field".'),
|
|
41
|
-
search: z
|
|
42
|
-
.string()
|
|
43
|
-
.max(100, "search must be 100 characters or fewer")
|
|
44
|
-
.regex(SEARCH_RE, "search must not contain control characters")
|
|
45
|
-
.optional()
|
|
46
|
-
.describe('Optional substring filter applied to "list_objects" results.'),
|
|
47
|
-
};
|
|
13
|
+
const inputSchema = DISCOVER_INPUT.shape;
|
|
48
14
|
|
|
49
15
|
export function registerSfGqlDiscoverTool(
|
|
50
16
|
server: McpServer,
|
|
@@ -5,54 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { childRelationshipSchema, graphqlName, orderByObject } from "./shared/zod-schemas.js";
|
|
10
8
|
import { buildList } from "../../intent/build-list.js";
|
|
11
9
|
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
10
|
+
import { LIST_INPUT } from "../../schemas/input-schemas.js";
|
|
12
11
|
|
|
13
12
|
export interface SfGqlListToolOptions {
|
|
14
13
|
primeDeps?: PrimeDeps;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
// compatibility with early MCP clients that learned the array form.
|
|
19
|
-
const orderBySchema = z.union([orderByObject, z.array(orderByObject)]);
|
|
20
|
-
|
|
21
|
-
const inputSchema = {
|
|
22
|
-
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
23
|
-
object: graphqlName('SObject API name, e.g. "Account", "Case". Must be a valid GraphQL Name.'),
|
|
24
|
-
fields: z
|
|
25
|
-
.array(z.string())
|
|
26
|
-
.describe(
|
|
27
|
-
'Scalar field API names to select on the node. Dot-paths like "Owner.Name" are allowed.',
|
|
28
|
-
),
|
|
29
|
-
parentFields: z
|
|
30
|
-
.array(z.string())
|
|
31
|
-
.optional()
|
|
32
|
-
.describe('Dotted parent-relationship paths, e.g. "Account.Name".'),
|
|
33
|
-
childRelationships: z.array(childRelationshipSchema(orderBySchema)).optional(),
|
|
34
|
-
filter: z
|
|
35
|
-
.record(z.unknown())
|
|
36
|
-
.optional()
|
|
37
|
-
.describe(
|
|
38
|
-
"<Object>_Filter shape. String leaves matching $varName promote to typed query variables.",
|
|
39
|
-
),
|
|
40
|
-
orderBy: orderBySchema
|
|
41
|
-
.optional()
|
|
42
|
-
.describe(
|
|
43
|
-
"<Object>_OrderBy. Singleton object preferred; arrays are collapsed to the first entry.",
|
|
44
|
-
),
|
|
45
|
-
first: z
|
|
46
|
-
.number()
|
|
47
|
-
.int()
|
|
48
|
-
.positive()
|
|
49
|
-
.optional()
|
|
50
|
-
.describe("Top-level connection page size; defaults to 10."),
|
|
51
|
-
scope: z.string().optional().describe('Scope enum (e.g. "MINE", "EVERYTHING") or $varName.'),
|
|
52
|
-
operationName: graphqlName(
|
|
53
|
-
"Override the GraphQL operation name. Defaults to <Object>List.",
|
|
54
|
-
).optional(),
|
|
55
|
-
};
|
|
16
|
+
const inputSchema = LIST_INPUT.shape;
|
|
56
17
|
|
|
57
18
|
export function registerSfGqlListTool(server: McpServer, opts: SfGqlListToolOptions = {}): void {
|
|
58
19
|
server.registerTool(
|
|
@@ -5,38 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { graphqlName } from "./shared/zod-schemas.js";
|
|
10
8
|
import { buildRaw } from "../../intent/build-raw.js";
|
|
11
9
|
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
10
|
+
import { RAW_INPUT } from "../../schemas/input-schemas.js";
|
|
12
11
|
|
|
13
12
|
export interface SfGqlRawToolOptions {
|
|
14
13
|
primeDeps?: PrimeDeps;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
const inputSchema =
|
|
18
|
-
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
19
|
-
commands: z
|
|
20
|
-
.array(z.string())
|
|
21
|
-
.min(1)
|
|
22
|
-
.describe(
|
|
23
|
-
"CLI-style commands applied in order to a transient session. Supported verbs:\n" +
|
|
24
|
-
" select <path[:alias]> e.g. select uiapi/query/Case/edges/node/Subject/value\n" +
|
|
25
|
-
" set [<path>] <key>=<value> e.g. set uiapi/query/Case first=10 | set uiapi/query/Case where.Status=New\n" +
|
|
26
|
-
" var $name <path> [default] e.g. var $id uiapi/query/Case/@args/where/Id/eq (type inferred from path)\n" +
|
|
27
|
-
'Each command is tokenized on spaces and a value MUST NOT contain a space — quoting does not help once a token has started (key=\'a b\' still splits). A filter value that contains a space (e.g. "New York", "In Progress") cannot be expressed via set in v1; use sf_gql_list with a JSON filter, or pass the value through a variable bound with var.\n' +
|
|
28
|
-
"Fails fast: a bad command aborts the whole call. Other CLI verbs (cd, drop, alias, optional, unset) are NOT supported in v1.",
|
|
29
|
-
),
|
|
30
|
-
operation: z
|
|
31
|
-
.enum(["query", "mutation", "aggregate"])
|
|
32
|
-
.optional()
|
|
33
|
-
.describe(
|
|
34
|
-
'Operation root. "query" (default) → uiapi.query; "mutation" → mutation root; "aggregate" → uiapi.aggregate.',
|
|
35
|
-
),
|
|
36
|
-
typeName: graphqlName(
|
|
37
|
-
"Override the GraphQL operation name. Defaults to Raw<Operation>.",
|
|
38
|
-
).optional(),
|
|
39
|
-
};
|
|
16
|
+
const inputSchema = RAW_INPUT.shape;
|
|
40
17
|
|
|
41
18
|
export function registerSfGqlRawTool(server: McpServer, opts: SfGqlRawToolOptions = {}): void {
|
|
42
19
|
server.registerTool(
|
|
@@ -5,34 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { graphqlName } from "./shared/zod-schemas.js";
|
|
10
8
|
import { buildUpdate } from "../../intent/build-update.js";
|
|
11
9
|
import { type PrimeDeps } from "../../lib/prime-schema.js";
|
|
10
|
+
import { UPDATE_INPUT } from "../../schemas/input-schemas.js";
|
|
12
11
|
|
|
13
12
|
export interface SfGqlUpdateToolOptions {
|
|
14
13
|
primeDeps?: PrimeDeps;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
const inputSchema =
|
|
18
|
-
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
19
|
-
object: graphqlName('SObject API name, e.g. "Account", "Order". Must be a valid GraphQL Name.'),
|
|
20
|
-
returnFields: z
|
|
21
|
-
.array(z.string())
|
|
22
|
-
.optional()
|
|
23
|
-
.describe(
|
|
24
|
-
'Scalar field API names to read back on the updated Record. Defaults to ["Id"]. Dot-paths like Owner.Name are not supported in mutation results; passing them produces a warning and the field is skipped.',
|
|
25
|
-
),
|
|
26
|
-
inputVariable: z
|
|
27
|
-
.string()
|
|
28
|
-
.optional()
|
|
29
|
-
.describe(
|
|
30
|
-
'Variable name (without "$") for the update input. Defaults to "input". Declared as $<name>: <Object>UpdateInput!.',
|
|
31
|
-
),
|
|
32
|
-
operationName: graphqlName(
|
|
33
|
-
"Override the GraphQL operation name. Defaults to Update<Object>.",
|
|
34
|
-
).optional(),
|
|
35
|
-
};
|
|
16
|
+
const inputSchema = UPDATE_INPUT.shape;
|
|
36
17
|
|
|
37
18
|
export function registerSfGqlUpdateTool(
|
|
38
19
|
server: McpServer,
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Per-tool Zod input schemas, extracted so the MCP server and the CLI mirror
|
|
9
|
+
* (`src/commands/mcp-mirror/`) validate identical argument shapes. The MCP
|
|
10
|
+
* tools consume `<TOOL>_INPUT.shape` (raw-shape registration) or the object
|
|
11
|
+
* directly (`detail`, which is `.strict()`); the CLI adapters call
|
|
12
|
+
* `<TOOL>_INPUT.parse()` on the incoming JSON. Single source of truth keeps
|
|
13
|
+
* the two transports arg-for-arg symmetric.
|
|
14
|
+
*
|
|
15
|
+
* Descriptions and validation are preserved verbatim from the original inline
|
|
16
|
+
* schemas; behavior is unchanged — this is a pure extraction.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { childRelationshipSchema, graphqlName, orderByObject, orgAlias } from "./fields.js";
|
|
21
|
+
import { GROUP_BY_FUNCTIONS } from "../intent/types.js";
|
|
22
|
+
|
|
23
|
+
// --- sf_gql_list ------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
// `sf_gql_list` advertises both shapes (singleton + array) for backward
|
|
26
|
+
// compatibility with early MCP clients that learned the array form.
|
|
27
|
+
const listOrderBySchema = z.union([orderByObject, z.array(orderByObject)]);
|
|
28
|
+
|
|
29
|
+
export const LIST_INPUT = z.object({
|
|
30
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
31
|
+
object: graphqlName('SObject API name, e.g. "Account", "Case". Must be a valid GraphQL Name.'),
|
|
32
|
+
fields: z
|
|
33
|
+
.array(z.string())
|
|
34
|
+
.describe(
|
|
35
|
+
'Scalar field API names to select on the node. Dot-paths like "Owner.Name" are allowed.',
|
|
36
|
+
),
|
|
37
|
+
parentFields: z
|
|
38
|
+
.array(z.string())
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Dotted parent-relationship paths, e.g. "Account.Name".'),
|
|
41
|
+
childRelationships: z.array(childRelationshipSchema(listOrderBySchema)).optional(),
|
|
42
|
+
filter: z
|
|
43
|
+
.record(z.unknown())
|
|
44
|
+
.optional()
|
|
45
|
+
.describe(
|
|
46
|
+
"<Object>_Filter shape. String leaves matching $varName promote to typed query variables.",
|
|
47
|
+
),
|
|
48
|
+
orderBy: listOrderBySchema
|
|
49
|
+
.optional()
|
|
50
|
+
.describe(
|
|
51
|
+
"<Object>_OrderBy. Singleton object preferred; arrays are collapsed to the first entry.",
|
|
52
|
+
),
|
|
53
|
+
first: z
|
|
54
|
+
.number()
|
|
55
|
+
.int()
|
|
56
|
+
.positive()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Top-level connection page size; defaults to 10."),
|
|
59
|
+
scope: z.string().optional().describe('Scope enum (e.g. "MINE", "EVERYTHING") or $varName.'),
|
|
60
|
+
operationName: graphqlName(
|
|
61
|
+
"Override the GraphQL operation name. Defaults to <Object>List.",
|
|
62
|
+
).optional(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// --- sf_gql_detail ----------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
// Advertised schema per FR-6.3 is the singleton object form; we still accept
|
|
68
|
+
// arrays at runtime via `z.preprocess` (FR-6.2 compat shim for early MCP
|
|
69
|
+
// clients that learned the array shape). The transform collapses to the first
|
|
70
|
+
// element before validation, so downstream code sees a singleton.
|
|
71
|
+
const detailOrderBySchema = z.preprocess((v) => (Array.isArray(v) ? v[0] : v), orderByObject);
|
|
72
|
+
|
|
73
|
+
// Note: filter/orderBy/scope/first are intentionally absent at the top level —
|
|
74
|
+
// `sf_gql_detail` is single-record-by-Id (FR-5.5). Filtering happens via the
|
|
75
|
+
// declared `$<idVariable>: ID!`; pagination doesn't apply.
|
|
76
|
+
//
|
|
77
|
+
// `.strict()` rejects unknown top-level keys at the MCP boundary so an LLM
|
|
78
|
+
// hallucinating sibling-tool params (`filter`, `scope`, `first`) gets a
|
|
79
|
+
// validation error to learn from rather than silent omission.
|
|
80
|
+
export const DETAIL_INPUT = z
|
|
81
|
+
.object({
|
|
82
|
+
org: orgAlias("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
83
|
+
object: graphqlName('SObject API name, e.g. "Account", "Case". Must be a valid GraphQL Name.'),
|
|
84
|
+
fields: z
|
|
85
|
+
.array(z.string())
|
|
86
|
+
.describe(
|
|
87
|
+
'Scalar field API names to select on the node. Dot-paths like "Owner.Name" are allowed.',
|
|
88
|
+
),
|
|
89
|
+
parentFields: z
|
|
90
|
+
.array(z.string())
|
|
91
|
+
.optional()
|
|
92
|
+
.describe('Dotted parent-relationship paths, e.g. "Account.Name".'),
|
|
93
|
+
childRelationships: z.array(childRelationshipSchema(detailOrderBySchema)).optional(),
|
|
94
|
+
idVariable: graphqlName(
|
|
95
|
+
'Name (without leading "$") for the ID variable. Defaults to "id". Declared as <name>: ID! and bound via where { Id: { eq: $<name> } } per FR-5.5. Choose a name that does not appear as a $varName in any childRelationships filter/orderBy — collisions are rejected.',
|
|
96
|
+
).optional(),
|
|
97
|
+
operationName: graphqlName(
|
|
98
|
+
"Override the GraphQL operation name. Defaults to <Object>Detail.",
|
|
99
|
+
).optional(),
|
|
100
|
+
})
|
|
101
|
+
.strict();
|
|
102
|
+
|
|
103
|
+
// --- sf_gql_discover --------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
// Org/object/field flow into shell commands (sf CLI), filesystem paths
|
|
106
|
+
// (ObjectInfo cache), and GraphQL operation names. Bound at the MCP boundary
|
|
107
|
+
// to safe API-name charsets so downstream layers can trust them.
|
|
108
|
+
const DISCOVER_ORG_ALIAS_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
109
|
+
const DISCOVER_SOBJECT_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,79}$/;
|
|
110
|
+
// eslint-disable-next-line no-control-regex -- intentionally rejects control chars
|
|
111
|
+
const DISCOVER_SEARCH_RE = /^[^\x00-\x1f\x7f]*$/;
|
|
112
|
+
|
|
113
|
+
export const DISCOVER_INPUT = z.object({
|
|
114
|
+
org: z
|
|
115
|
+
.string()
|
|
116
|
+
.regex(DISCOVER_ORG_ALIAS_RE, "org must match /^[A-Za-z0-9_-]{1,80}$/")
|
|
117
|
+
.describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
118
|
+
mode: z
|
|
119
|
+
.enum(["list_objects", "describe_object", "describe_field"])
|
|
120
|
+
.describe(
|
|
121
|
+
'Discovery mode. "list_objects" enumerates queryable SObjects; "describe_object" and "describe_field" return ObjectInfo metadata.',
|
|
122
|
+
),
|
|
123
|
+
object: z
|
|
124
|
+
.string()
|
|
125
|
+
.regex(DISCOVER_SOBJECT_NAME_RE, "object must match /^[A-Za-z][A-Za-z0-9_]{0,79}$/")
|
|
126
|
+
.optional()
|
|
127
|
+
.describe('SObject API name. Required for "describe_object" and "describe_field".'),
|
|
128
|
+
field: z
|
|
129
|
+
.string()
|
|
130
|
+
.regex(DISCOVER_SOBJECT_NAME_RE, "field must match /^[A-Za-z][A-Za-z0-9_]{0,79}$/")
|
|
131
|
+
.optional()
|
|
132
|
+
.describe('Field API name. Required for "describe_field".'),
|
|
133
|
+
search: z
|
|
134
|
+
.string()
|
|
135
|
+
.max(100, "search must be 100 characters or fewer")
|
|
136
|
+
.regex(DISCOVER_SEARCH_RE, "search must not contain control characters")
|
|
137
|
+
.optional()
|
|
138
|
+
.describe('Optional substring filter applied to "list_objects" results.'),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// --- sf_gql_aggregate -------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
const aliasField = z
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("GraphQL alias for this aggregation's result key.");
|
|
147
|
+
|
|
148
|
+
const aggregationSchema = z.discriminatedUnion("function", [
|
|
149
|
+
z.object({
|
|
150
|
+
function: z.enum(["count", "countDistinct"]),
|
|
151
|
+
field: z.string().optional().describe('SObject field API name. Defaults to "Id" when omitted.'),
|
|
152
|
+
alias: aliasField,
|
|
153
|
+
}),
|
|
154
|
+
z.object({
|
|
155
|
+
function: z.enum(["sum", "avg", "min", "max"]),
|
|
156
|
+
field: z.string().describe("SObject field API name. Required for sum/avg/min/max."),
|
|
157
|
+
alias: aliasField,
|
|
158
|
+
}),
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const groupByElementSchema = z.union([
|
|
162
|
+
z.string(),
|
|
163
|
+
z.object({
|
|
164
|
+
field: z.string().describe("SObject field API name (DateTime/Date field)."),
|
|
165
|
+
function: z
|
|
166
|
+
.enum(GROUP_BY_FUNCTIONS)
|
|
167
|
+
.describe("Date bucketing function from UIAPI GroupByFunction enum."),
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
export const AGGREGATE_INPUT = z.object({
|
|
172
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
173
|
+
object: graphqlName('SObject API name, e.g. "Account", "Order". Must be a valid GraphQL Name.'),
|
|
174
|
+
groupBy: z
|
|
175
|
+
.array(groupByElementSchema)
|
|
176
|
+
.optional()
|
|
177
|
+
.describe(
|
|
178
|
+
"GroupBy elements. Each is either a plain field name string (renders as `{Field: {group: true}}`) or an object `{field, function}` for date-bucketed groupBy (renders as `{Field: {function: CALENDAR_MONTH}}`). Omit or pass `[]` for un-grouped aggregation; with no aggregations either, defaults to `count` over `Id` (FR-8.2).",
|
|
179
|
+
),
|
|
180
|
+
aggregations: z
|
|
181
|
+
.array(aggregationSchema)
|
|
182
|
+
.optional()
|
|
183
|
+
.describe(
|
|
184
|
+
"List of aggregation functions to compute. Each entry projects under its alias (or default `<function><PascalCaseField>`).",
|
|
185
|
+
),
|
|
186
|
+
filter: z
|
|
187
|
+
.record(z.unknown())
|
|
188
|
+
.optional()
|
|
189
|
+
.describe(
|
|
190
|
+
"<Object>_Filter applied to every aggregation. String leaves matching $varName promote to typed query variables.",
|
|
191
|
+
),
|
|
192
|
+
orderBy: z
|
|
193
|
+
.union([z.record(z.unknown()), z.array(z.record(z.unknown()))])
|
|
194
|
+
.optional()
|
|
195
|
+
.describe(
|
|
196
|
+
"<Object>_OrderBy. Singleton object preferred; arrays are collapsed to the first entry. String leaves matching $varName promote to typed query variables.",
|
|
197
|
+
),
|
|
198
|
+
first: z
|
|
199
|
+
.number()
|
|
200
|
+
.int()
|
|
201
|
+
.positive()
|
|
202
|
+
.optional()
|
|
203
|
+
.describe("Connection page size (top-N pattern). No default — omit for all buckets."),
|
|
204
|
+
operationName: graphqlName(
|
|
205
|
+
"Override the GraphQL operation name. Defaults to <Object>Aggregate.",
|
|
206
|
+
).optional(),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// --- sf_gql_raw -------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
export const RAW_INPUT = z.object({
|
|
212
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
213
|
+
commands: z
|
|
214
|
+
.array(z.string())
|
|
215
|
+
.min(1)
|
|
216
|
+
.describe(
|
|
217
|
+
"CLI-style commands applied in order to a transient session. Supported verbs:\n" +
|
|
218
|
+
" select <path[:alias]> e.g. select uiapi/query/Case/edges/node/Subject/value\n" +
|
|
219
|
+
" set [<path>] <key>=<value> e.g. set uiapi/query/Case first=10 | set uiapi/query/Case where.Status=New\n" +
|
|
220
|
+
" var $name <path> [default] e.g. var $id uiapi/query/Case/@args/where/Id/eq (type inferred from path)\n" +
|
|
221
|
+
'Each command is tokenized on spaces and a value MUST NOT contain a space — quoting does not help once a token has started (key=\'a b\' still splits). A filter value that contains a space (e.g. "New York", "In Progress") cannot be expressed via set in v1; use sf_gql_list with a JSON filter, or pass the value through a variable bound with var.\n' +
|
|
222
|
+
"Fails fast: a bad command aborts the whole call. Other CLI verbs (cd, drop, alias, optional, unset) are NOT supported in v1.",
|
|
223
|
+
),
|
|
224
|
+
operation: z
|
|
225
|
+
.enum(["query", "mutation", "aggregate"])
|
|
226
|
+
.optional()
|
|
227
|
+
.describe(
|
|
228
|
+
'Operation root. "query" (default) → uiapi.query; "mutation" → mutation root; "aggregate" → uiapi.aggregate.',
|
|
229
|
+
),
|
|
230
|
+
typeName: graphqlName(
|
|
231
|
+
"Override the GraphQL operation name. Defaults to Raw<Operation>.",
|
|
232
|
+
).optional(),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// --- sf_gql_create ----------------------------------------------------------
|
|
236
|
+
|
|
237
|
+
export const CREATE_INPUT = z.object({
|
|
238
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
239
|
+
object: graphqlName('SObject API name, e.g. "Account", "Order". Must be a valid GraphQL Name.'),
|
|
240
|
+
returnFields: z
|
|
241
|
+
.array(z.string())
|
|
242
|
+
.optional()
|
|
243
|
+
.describe(
|
|
244
|
+
'Scalar field API names to read back on the created Record. Defaults to ["Id"]. Dot-paths like Owner.Name are not supported in mutation results; passing them produces a warning and the field is skipped.',
|
|
245
|
+
),
|
|
246
|
+
inputVariable: z
|
|
247
|
+
.string()
|
|
248
|
+
.optional()
|
|
249
|
+
.describe(
|
|
250
|
+
'Variable name (without "$") for the create input. Defaults to "input". Declared as $<name>: <Object>CreateInput!.',
|
|
251
|
+
),
|
|
252
|
+
operationName: graphqlName(
|
|
253
|
+
"Override the GraphQL operation name. Defaults to Create<Object>.",
|
|
254
|
+
).optional(),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// --- sf_gql_update ----------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
export const UPDATE_INPUT = z.object({
|
|
260
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
261
|
+
object: graphqlName('SObject API name, e.g. "Account", "Order". Must be a valid GraphQL Name.'),
|
|
262
|
+
returnFields: z
|
|
263
|
+
.array(z.string())
|
|
264
|
+
.optional()
|
|
265
|
+
.describe(
|
|
266
|
+
'Scalar field API names to read back on the updated Record. Defaults to ["Id"]. Dot-paths like Owner.Name are not supported in mutation results; passing them produces a warning and the field is skipped.',
|
|
267
|
+
),
|
|
268
|
+
inputVariable: z
|
|
269
|
+
.string()
|
|
270
|
+
.optional()
|
|
271
|
+
.describe(
|
|
272
|
+
'Variable name (without "$") for the update input. Defaults to "input". Declared as $<name>: <Object>UpdateInput!.',
|
|
273
|
+
),
|
|
274
|
+
operationName: graphqlName(
|
|
275
|
+
"Override the GraphQL operation name. Defaults to Update<Object>.",
|
|
276
|
+
).optional(),
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// --- sf_gql_delete ----------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
export const DELETE_INPUT = z.object({
|
|
282
|
+
org: z.string().describe("Org alias resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
283
|
+
object: graphqlName('SObject API name, e.g. "Account", "Order". Must be a valid GraphQL Name.'),
|
|
284
|
+
inputVariable: z
|
|
285
|
+
.string()
|
|
286
|
+
.optional()
|
|
287
|
+
.describe(
|
|
288
|
+
'Variable name (without "$") for the delete input. Defaults to "input". Declared as $<name>: RecordDeleteInput! — the schema-wide delete input carrying the record Id, not an <Object>-specific type.',
|
|
289
|
+
),
|
|
290
|
+
operationName: graphqlName(
|
|
291
|
+
"Override the GraphQL operation name. Defaults to Delete<Object>.",
|
|
292
|
+
).optional(),
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// --- sf_gql_connect ---------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
export const CONNECT_INPUT = z.object({
|
|
298
|
+
org: orgAlias("Org alias or username resolved via local Salesforce CLI auth (~/.sf, ~/.sfdx)."),
|
|
299
|
+
forceRefresh: z
|
|
300
|
+
.boolean()
|
|
301
|
+
.optional()
|
|
302
|
+
.describe(
|
|
303
|
+
"Re-download the schema even if it is already cached, clearing the on-disk introspection JSON, the in-memory parsed schema, and the ObjectInfo cache. Use after deploying new metadata (fields, picklist values, objects) so subsequent tools see the changes. Defaults to false.",
|
|
304
|
+
),
|
|
305
|
+
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zod-schemas.js","sourceRoot":"","sources":["../../../../src/mcp/tools/shared/zod-schemas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC/D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,8BAA8B,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAEzF;;;;;GAKG;AACH,MAAM,YAAY,GAAG,uCAAuC,CAAC;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAe,EAAE,CAC5D,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,uCAAuC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAE/F;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAqB,EAAE,EAAE,CAChE,CAAC,CAAC,MAAM,CAAC;IACR,gBAAgB,EAAE,WAAW,CAC5B,8FAA8F,CAC9F;IACD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;IACxC,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC"}
|
|
@@ -35,14 +35,14 @@ export declare const childRelationshipSchema: (orderBy: z.ZodTypeAny) => z.ZodOb
|
|
|
35
35
|
filter: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
36
36
|
orderBy: z.ZodOptional<z.ZodTypeAny>;
|
|
37
37
|
}, "strip", z.ZodTypeAny, {
|
|
38
|
-
fields: string[];
|
|
39
38
|
relationshipName: string;
|
|
39
|
+
fields: string[];
|
|
40
40
|
filter?: Record<string, unknown> | undefined;
|
|
41
41
|
orderBy?: any;
|
|
42
42
|
first?: number | undefined;
|
|
43
43
|
}, {
|
|
44
|
-
fields: string[];
|
|
45
44
|
relationshipName: string;
|
|
45
|
+
fields: string[];
|
|
46
46
|
filter?: Record<string, unknown> | undefined;
|
|
47
47
|
orderBy?: any;
|
|
48
48
|
first?: number | undefined;
|