@bridge_gpt/mcp-server 0.2.37 → 0.2.38

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.
@@ -11,17 +11,31 @@
11
11
  * OCAPI's CustomObjectDefinitions resource only exposes operations scoped to
12
12
  * a known object_type — there is no top-level list and no bare get-by-type
13
13
  * (unlike SystemObjectDefinitions). A caller-supplied object_type is always
14
- * required; OCAPI cannot enumerate custom object types.
14
+ * required; OCAPI cannot enumerate custom object type IDs directly.
15
+ *
16
+ * That is narrower than "wholly undiscoverable," and the tools below say so:
17
+ * `system_object_list` at `projection: "full"` returns a distinct
18
+ * `display_name` and `attribute_definition_count` per custom type, from which a
19
+ * candidate ID can be derived (e.g. strip spaces from "Product Quality Result"
20
+ * → "ProductQualityResult") and confirmed by checking that this module's own
21
+ * `custom_object_definition_attributes_get` returns an attribute count matching
22
+ * that row's `attribute_definition_count`. This adds no new tool and no new
23
+ * projection value — it is existing capability, stated accurately.
15
24
  *
16
25
  * Note: custom object TYPE creation/update is NOT available via OCAPI REST.
17
26
  * It is a v2 metadata-import capability (metadata XML → WebDAV → import job).
27
+ *
28
+ * Both tools can return complete attribute-definition documents at
29
+ * `projection: "full"` (BAPI-767). Bridge withholds each attribute's
30
+ * `default_value` from that response (BAPI-769, `read-body.ts`) — every other
31
+ * schema field (type, flags, labels) is unaffected.
18
32
  */
19
33
  import path from "path";
20
34
  import { z } from "zod";
21
35
  import { ocapiGet, ocapiPost } from "./client.js";
22
36
  import { withSfccGate } from "./tool-wrapper.js";
23
37
  import { formatOcapiReadFailure, withSfccReadErrorBoundary } from "./read-result.js";
24
- import { normalizeOcapiBody } from "./ocapi-shape.js";
38
+ import { readSfccBody } from "./read-body.js";
25
39
  import { applyProjectionToBody, projectionInputFor, projectionQueryEntries, } from "./read-projection.js";
26
40
  import { truncateAndSaveIfNeeded } from "./output.js";
27
41
  // ---------------------------------------------------------------------------
@@ -36,18 +50,17 @@ const READ_ANNOTATIONS = {
36
50
  // ---------------------------------------------------------------------------
37
51
  // Input schemas
38
52
  // ---------------------------------------------------------------------------
53
+ const OBJECT_TYPE_DISCOVERY_HINT = 'Known custom object type id (e.g. starting with "c_"). OCAPI cannot enumerate ' +
54
+ "type IDs directly, but you can discover a candidate via system_object_list at " +
55
+ "projection=\"full\" (distinct display_name + attribute_definition_count per " +
56
+ "custom type) and confirm it by checking that this tool's returned attribute " +
57
+ "count matches that row's attribute_definition_count.";
39
58
  const customObjectAttributesGetInput = z.object({
40
- object_type: z
41
- .string()
42
- .describe('Known custom object type identifier, e.g. a custom type id starting with "c_". ' +
43
- "OCAPI cannot enumerate custom object types — the type must already be known."),
59
+ object_type: z.string().describe(OBJECT_TYPE_DISCOVERY_HINT),
44
60
  projection: projectionInputFor("attribute_collection"),
45
61
  });
46
62
  const customObjectAttributeSearchInput = z.object({
47
- object_type: z
48
- .string()
49
- .describe('Known custom object type to search within, e.g. a custom type id starting with "c_". ' +
50
- "OCAPI cannot enumerate custom object types — the type must already be known."),
63
+ object_type: z.string().describe(OBJECT_TYPE_DISCOVERY_HINT),
51
64
  query: z.union([z.string(), z.record(z.string(), z.any())]).describe("Search query. A plain string is a case-insensitive substring match across " +
52
65
  "id and display_name; a structured OCAPI query object passes through unchanged."),
53
66
  start: z.number().optional().describe("Zero-based offset for paging."),
@@ -86,7 +99,7 @@ function buildCustomObjectAttributesGetHandler(gateDeps, getDocsDir) {
86
99
  if (!result.ok) {
87
100
  return formatOcapiReadFailure(result);
88
101
  }
89
- const normalized = normalizeOcapiBody(result.body);
102
+ const normalized = readSfccBody(result.body);
90
103
  const text = JSON.stringify(normalized, null, 2);
91
104
  const dir = path.join(await getDocsDir(), "sfcc");
92
105
  return saveAndReturn(text, dir, `custom-object-def-attributes-${safeType(object_type)}-${safeTimestamp()}.json`, normalized.page);
@@ -112,7 +125,7 @@ function buildCustomObjectAttributeSearchHandler(gateDeps, getDocsDir) {
112
125
  if (!result.ok) {
113
126
  return formatOcapiReadFailure(result);
114
127
  }
115
- const normalized = normalizeOcapiBody(result.body);
128
+ const normalized = readSfccBody(result.body);
116
129
  const text = JSON.stringify(normalized, null, 2);
117
130
  const dir = path.join(await getDocsDir(), "sfcc");
118
131
  return saveAndReturn(text, dir, `custom-object-def-search-${safeType(object_type)}-${safeTimestamp()}.json`, normalized.page);
@@ -127,20 +140,19 @@ export function registerSfccCustomObjectDefReadTools(registerTool, deps) {
127
140
  const { gateDeps, getDocsDir } = deps;
128
141
  registerTool("custom_object_definition_attributes_get", {
129
142
  description: "Retrieve attribute definitions for a KNOWN custom object type via " +
130
- "GET /custom_object_definitions/{type}/attribute_definitions. " +
131
- "Returns each attribute's full schema by default. " +
132
- "OCAPI cannot enumerate types object_type is required. " +
133
- "Type creation is v2 metadata-import only. Oversized payloads auto-saved locally.",
143
+ "GET /custom_object_definitions/{type}/attribute_definitions " +
144
+ "(default_value withheld). OCAPI cannot enumerate type IDs directly; " +
145
+ "derive one via system_object_list (projection=full, attribute_definition_count), " +
146
+ "confirm here. Type creation is v2 metadata-import only.",
134
147
  inputSchema: customObjectAttributesGetInput,
135
148
  annotations: READ_ANNOTATIONS,
136
149
  }, buildCustomObjectAttributesGetHandler(gateDeps, getDocsDir));
137
150
  registerTool("custom_object_definition_attribute_search", {
138
151
  description: "Search attribute definitions within a KNOWN custom object type via " +
139
- "POST /custom_object_definitions/{type}/attribute_definition_search. " +
140
- "Returns each attribute's full schema by default. " +
141
- "OCAPI cannot enumerate types; object_type is required. " +
142
- "Pass a string or OCAPI query. Type creation is v2 metadata-import only. " +
143
- "Oversized results auto-saved locally.",
152
+ "POST /custom_object_definitions/{type}/attribute_definition_search " +
153
+ "(default_value withheld). OCAPI cannot enumerate type IDs directly; " +
154
+ "derive one via system_object_list (projection=full, attribute_definition_count), " +
155
+ "confirm here. Type creation is v2 metadata-import only.",
144
156
  inputSchema: customObjectAttributeSearchInput,
145
157
  annotations: READ_ANNOTATIONS,
146
158
  }, buildCustomObjectAttributeSearchHandler(gateDeps, getDocsDir));
@@ -24,11 +24,18 @@
24
24
  * It is also, for now, accidentally protective. A name-heuristic scan of all 19
25
25
  * preference groups on the tested sandbox found secret-named preferences in six
26
26
  * of them — including one literally named `client_id` — and none of them expose
27
- * a value through this endpoint. No redaction seam is built here because there
28
- * is no value-bearing path to redact. If OCAPI ever starts returning values,
27
+ * a value through this endpoint. If OCAPI ever starts returning values here,
29
28
  * redaction must be added on the raw body BEFORE normalization, serialization,
30
29
  * truncation, or save, so one placement covers both the inline response and the
31
- * docs/tmp/sfcc/ save path.
30
+ * docs/tmp/sfcc/ save path — exactly the `readSfccBody` seam these three
31
+ * handlers already route through (BAPI-769).
32
+ *
33
+ * That value-bearing route already exists elsewhere: `SitePreferences`
34
+ * attribute-definition reads via `system_object_attribute_search` (with
35
+ * `object_type: "SitePreferences"`) can return each preference's
36
+ * `default_value`, and `readSfccBody` withholds it there today. This module's
37
+ * three preference-value tools remain ids-only for the reason stated above —
38
+ * OCAPI's `preference_search` itself never returns a value, `select` or not.
32
39
  *
33
40
  * NOTE: SCAPI Preferences API (sfcc.preferences) is intentionally NOT used.
34
41
  * This keeps the tool context on the OCAPI Data API, preserving the single
@@ -40,7 +47,7 @@ import { z } from "zod";
40
47
  import { ocapiGet, ocapiPost } from "./client.js";
41
48
  import { withSfccGate } from "./tool-wrapper.js";
42
49
  import { formatOcapiReadFailure, formatSfccReadFailure, withSfccReadErrorBoundary, } from "./read-result.js";
43
- import { normalizeOcapiBody } from "./ocapi-shape.js";
50
+ import { readSfccBody } from "./read-body.js";
44
51
  import { projectionInputFor, projectionQueryEntries } from "./read-projection.js";
45
52
  import { truncateAndSaveIfNeeded } from "./output.js";
46
53
  // ---------------------------------------------------------------------------
@@ -145,7 +152,7 @@ export function buildSitePreferenceGetHandler(gateDeps, getDocsDir) {
145
152
  if (!result.ok) {
146
153
  return formatOcapiReadFailure(result);
147
154
  }
148
- const normalized = normalizeOcapiBody(result.body);
155
+ const normalized = readSfccBody(result.body);
149
156
  const text = JSON.stringify(normalized, null, 2);
150
157
  const dir = path.join(await getDocsDir(), "sfcc");
151
158
  return saveAndReturn(text, dir, `site-preference-get-${safeGroup(group)}-${safeTimestamp()}.json`, normalized.page);
@@ -173,7 +180,7 @@ export function buildSitePreferenceSearchHandler(gateDeps, getDocsDir) {
173
180
  if (!result.ok) {
174
181
  return formatOcapiReadFailure(result);
175
182
  }
176
- const normalized = normalizeOcapiBody(result.body);
183
+ const normalized = readSfccBody(result.body);
177
184
  const text = JSON.stringify(normalized, null, 2);
178
185
  const dir = path.join(await getDocsDir(), "sfcc");
179
186
  return saveAndReturn(text, dir, `site-preference-search-${safeGroup(group)}-${safeTimestamp()}.json`, normalized.page);
@@ -196,7 +203,7 @@ export function buildSitePreferenceGroupListHandler(gateDeps, getDocsDir) {
196
203
  if (!result.ok) {
197
204
  return formatOcapiReadFailure(result);
198
205
  }
199
- const normalized = normalizeOcapiBody(result.body);
206
+ const normalized = readSfccBody(result.body);
200
207
  const text = JSON.stringify(normalized, null, 2);
201
208
  const dir = path.join(await getDocsDir(), "sfcc");
202
209
  return saveAndReturn(text, dir, `site-preference-group-list-${safeTimestamp()}.json`, normalized.page);
@@ -8,13 +8,18 @@
8
8
  *
9
9
  * All tools are read-only, run behind the T1 call-time gate, and route
10
10
  * oversized payloads through the sfcc/output.ts truncate-and-save seam.
11
+ *
12
+ * `system_object_attribute_search` can return complete attribute-definition
13
+ * documents at `projection: "full"` (BAPI-767). Bridge withholds each
14
+ * attribute's `default_value` from that response (BAPI-769, `read-body.ts`) —
15
+ * every other schema field (type, flags, labels) is unaffected.
11
16
  */
12
17
  import path from "path";
13
18
  import { z } from "zod";
14
19
  import { ocapiGet, ocapiPost } from "./client.js";
15
20
  import { withSfccGate } from "./tool-wrapper.js";
16
21
  import { formatOcapiReadFailure, withSfccReadErrorBoundary } from "./read-result.js";
17
- import { normalizeOcapiBody } from "./ocapi-shape.js";
22
+ import { readSfccBody } from "./read-body.js";
18
23
  import { applyProjectionToBody, projectionInputFor, projectionQueryEntries, } from "./read-projection.js";
19
24
  import { truncateAndSaveIfNeeded } from "./output.js";
20
25
  // ---------------------------------------------------------------------------
@@ -86,7 +91,7 @@ function buildSystemObjectListHandler(gateDeps, getDocsDir) {
86
91
  if (!result.ok) {
87
92
  return formatOcapiReadFailure(result);
88
93
  }
89
- const normalized = normalizeOcapiBody(result.body);
94
+ const normalized = readSfccBody(result.body);
90
95
  const text = JSON.stringify(normalized, null, 2);
91
96
  const dir = path.join(await getDocsDir(), "sfcc");
92
97
  return saveAndReturn(text, dir, `system-object-list-${safeTimestamp()}.json`, normalized.page);
@@ -100,7 +105,7 @@ function buildSystemObjectGetHandler(gateDeps, getDocsDir) {
100
105
  if (!result.ok) {
101
106
  return formatOcapiReadFailure(result);
102
107
  }
103
- const normalized = normalizeOcapiBody(result.body);
108
+ const normalized = readSfccBody(result.body);
104
109
  const text = JSON.stringify(normalized, null, 2);
105
110
  const dir = path.join(await getDocsDir(), "sfcc");
106
111
  return saveAndReturn(text, dir, `system-object-get-${safeType(object_type)}-${safeTimestamp()}.json`, normalized.page);
@@ -126,7 +131,7 @@ function buildSystemObjectAttributeSearchHandler(gateDeps, getDocsDir) {
126
131
  if (!result.ok) {
127
132
  return formatOcapiReadFailure(result);
128
133
  }
129
- const normalized = normalizeOcapiBody(result.body);
134
+ const normalized = readSfccBody(result.body);
130
135
  const text = JSON.stringify(normalized, null, 2);
131
136
  const dir = path.join(await getDocsDir(), "sfcc");
132
137
  return saveAndReturn(text, dir, `system-object-search-${safeType(object_type)}-${safeTimestamp()}.json`, normalized.page);
@@ -159,7 +164,8 @@ export function registerSystemObjectReadTools(registerTool, deps) {
159
164
  registerTool("system_object_attribute_search", {
160
165
  description: "Search attribute definitions for a system object type. Read-only. " +
161
166
  "POST /system_object_definitions/{type}/attribute_definition_search. " +
162
- "Use for targeted c_ attribute lookups; returns each attribute's full schema by default. " +
167
+ "Use for targeted c_ attribute lookups; returns each attribute's schema " +
168
+ "by default (default_value withheld). " +
163
169
  "Pass a plain string for text search or a structured OCAPI query. " +
164
170
  "Oversized results auto-saved locally.",
165
171
  inputSchema: systemObjectAttributeSearchInput,
@@ -6,15 +6,19 @@
6
6
  * their own 403/fault/success response logic, guaranteeing a consistent surface
7
7
  * across every write tool.
8
8
  *
9
- * Successful results are unchanged and never gain `isError`. Every *failure*
10
- * branch including the 403 write-grant remediation, which used to be prose —
11
- * now returns the universal nested envelope from `sfcc-result.ts`, so
12
- * `result.isError === true` is total across a write tool's post-transport
13
- * failures as well as its pre-transport ones.
9
+ * Successful results never gain `isError`, but their echoed `body` is redacted
10
+ * (BAPI-769): an attribute-definition write's success body can carry the same
11
+ * `default_value` a read can, so it is withheld via `read-body.ts`'s
12
+ * `redactSfccBody` before serialization. Every other *failure* branch
13
+ * including the 403 write-grant remediation, which used to be prose — returns
14
+ * the universal nested envelope from `sfcc-result.ts`, so `result.isError ===
15
+ * true` is total across a write tool's post-transport failures as well as its
16
+ * pre-transport ones.
14
17
  */
15
18
  import { writeGrantForbiddenResult } from "./write-grants.js";
16
19
  import { DEFAULT_OCAPI_VERSION } from "./config.js";
17
20
  import { formatSfccWriteFailure } from "./sfcc-result.js";
21
+ import { redactSfccBody } from "./read-body.js";
18
22
  /** Wrap text in the standard MCP text-result shape (success path only). */
19
23
  function textResult(text) {
20
24
  return { content: [{ type: "text", text }] };
@@ -54,7 +58,12 @@ function ocapiWriteFaultMessage(body, status) {
54
58
  *
55
59
  * - `403` → the unified failure carrying the paste-ready write-grant
56
60
  * remediation as structured data (via write-grants.ts).
57
- * - success → JSON with `status`, `outcome`, and `body`; no `isError`.
61
+ * - success → JSON with `status`, `outcome`, and `body`; no `isError`. `body`
62
+ * is redacted (BAPI-769) before serialization: a successful
63
+ * attribute-definition PUT/PATCH echoes the updated resource in the same
64
+ * shape a GET returns, so it can carry a real `default_value` exactly like a
65
+ * read can — `redactSfccBody` withholds it here the same way `readSfccBody`
66
+ * withholds it on the read path. Every other key survives untouched.
58
67
  * - other failure → the unified `OCAPI_WRITE_ERROR` failure, source `ocapi`,
59
68
  * with the structured fault mapping and the upstream body under
60
69
  * `error.details`.
@@ -76,7 +85,7 @@ export function formatOcapiWriteToolResult(result, operation, path, ocapiVersion
76
85
  return textResult(JSON.stringify({
77
86
  status: result.status,
78
87
  outcome: result.outcome,
79
- body: result.body,
88
+ body: redactSfccBody(result.body),
80
89
  }));
81
90
  }
82
91
  return formatSfccWriteFailure({
@@ -8,7 +8,10 @@
8
8
  * Both tools write ATTRIBUTE DEFINITIONS on an already-known custom object type.
9
9
  * Custom object TYPE creation is intentionally NOT attempted: OCAPI cannot
10
10
  * create custom object types (that is a v2 metadata-import capability), so the
11
- * type must pre-exist. Mirrors reads-custom-object-def.ts.
11
+ * type must pre-exist. OCAPI also cannot enumerate custom object type IDs
12
+ * directly — see reads-custom-object-def.ts for the `system_object_list` +
13
+ * `projection: "full"` discovery path a caller can use to find one before
14
+ * writing to it. Mirrors reads-custom-object-def.ts.
12
15
  *
13
16
  * Every handler is call-time gated by `withSfccGate`, sandbox-guarded via
14
17
  * `rejectIfNotSandboxForWrite`, and routes its OCAPI result through
@@ -31,7 +34,8 @@ const createCustomObjectAttributeDefinitionInput = z.object({
31
34
  object_type: z
32
35
  .string()
33
36
  .describe('Known custom object type identifier (must already exist). OCAPI cannot ' +
34
- "enumerate or create custom object types — only attribute definitions on a known type."),
37
+ "create types, and cannot enumerate type IDs directly — only attribute " +
38
+ "definitions on a known type."),
35
39
  attribute_id: z
36
40
  .string()
37
41
  .describe("URL attribute-definition id. If the body also carries `id`, it must match."),
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
2
- export const VERSION = "0.2.37";
2
+ export const VERSION = "0.2.38";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge_gpt/mcp-server",
3
- "version": "0.2.37",
3
+ "version": "0.2.38",
4
4
  "description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,7 +29,7 @@
29
29
  "test": "npm run test:normal && npm run test:module-mocks",
30
30
  "test:normal": "node scripts/run-unit-tests.js normal",
31
31
  "test:module-mocks": "node scripts/run-unit-tests.js module-mocks",
32
- "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/command-provisioning.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/implement-ticket-finalization-order.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js build/integration/request-council-trigger-drop.integration.test.js build/integration/install-bridge-onboarding-launch.integration.test.js build/integration/install-bridge-conductor.integration.test.js build/integration/install-bridge-failure-guards.integration.test.js build/integration/learn-repository-pipeline.integration.test.js build/integration/visual-diff-mcp.integration.test.js build/integration/executor-mcp-provisioning.integration.test.js build/integration/executor-worktree-reentry.integration.test.js build/integration/sfcc-read-error-contract.integration.test.js build/integration/sfcc-response-envelope.integration.test.js build/integration/sfcc-total-error-contract.integration.test.js build/integration/serve-stdio.integration.test.js build/integration/install-alias.integration.test.js build/integration/setup-epic-validation.integration.test.js build/integration/plane-cli.integration.test.js",
32
+ "test:integration": "node --test build/integration/refresh-main.integration.test.js build/integration/command-provisioning.integration.test.js build/integration/start-tickets.integration.test.js build/integration/start-tickets-tier-handoff.integration.test.js build/integration/doctor.integration.test.js build/integration/agent-capabilities.integration.test.js build/integration/conductor-producer.integration.test.js build/integration/conductor-message-relay.integration.test.js build/integration/executor-http-runner.integration.test.js build/integration/executor-job-behaviors.integration.test.js build/integration/executor-recovery-jobs.integration.test.js build/integration/executor-spec-review-prompt.integration.test.js build/integration/resume-pre-spawn.git.integration.test.js build/integration/worker-finalization-origin.integration.test.js build/integration/post-remediation-merge-ci-wait.integration.test.js build/integration/executor-merge-supervision.integration.test.js build/integration/attachment-binary-roundtrip.integration.test.js build/integration/dependent-ticket-fresh-base.integration.test.js build/integration/execute-plan-instructions.integration.test.js build/integration/implement-ticket-finalization-order.integration.test.js build/integration/conductor-bundle-artifacts.integration.test.js build/integration/install-bridge-repo-resolution.integration.test.js build/integration/capability-report-contract.integration.test.js build/integration/request-brainstorm-general.integration.test.js build/integration/request-council-trigger-drop.integration.test.js build/integration/install-bridge-onboarding-launch.integration.test.js build/integration/install-bridge-conductor.integration.test.js build/integration/install-bridge-failure-guards.integration.test.js build/integration/learn-repository-pipeline.integration.test.js build/integration/visual-diff-mcp.integration.test.js build/integration/executor-mcp-provisioning.integration.test.js build/integration/executor-worktree-reentry.integration.test.js build/integration/sfcc-read-error-contract.integration.test.js build/integration/sfcc-read-surface.integration.test.js build/integration/sfcc-response-envelope.integration.test.js build/integration/sfcc-total-error-contract.integration.test.js build/integration/serve-stdio.integration.test.js build/integration/install-alias.integration.test.js build/integration/setup-epic-validation.integration.test.js build/integration/plane-cli.integration.test.js",
33
33
  "test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
34
34
  "canary:agent-capabilities": "npm run build && node scripts/agent-capabilities-canary.mjs",
35
35
  "prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"