@appaflytech/zeyrek-ai-mcp 0.0.28 → 0.0.30

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.
Files changed (201) hide show
  1. package/.env.example +15 -0
  2. package/README.md +5 -5
  3. package/dist/config.js +37 -0
  4. package/dist/index.js +2 -0
  5. package/dist/knowledge/access.js +802 -0
  6. package/dist/knowledge/backendConstraints.js +18 -1
  7. package/dist/knowledge/case.js +41 -0
  8. package/dist/knowledge/componentCatalog.js +494 -34
  9. package/dist/knowledge/componentEvents.js +132 -7
  10. package/dist/knowledge/connectDataBind.js +819 -0
  11. package/dist/knowledge/connectDataDefinition.js +341 -0
  12. package/dist/knowledge/connectDataExecute.js +93 -0
  13. package/dist/knowledge/connectDataRules.js +128 -0
  14. package/dist/knowledge/decision.js +121 -0
  15. package/dist/knowledge/expressionPrelude.js +59 -0
  16. package/dist/knowledge/layoutSummary.js +140 -2
  17. package/dist/knowledge/microserviceParameters.js +58 -0
  18. package/dist/knowledge/mining.js +94 -0
  19. package/dist/knowledge/mobileComponentCatalog.js +859 -0
  20. package/dist/knowledge/mobileComponentEvents.js +239 -0
  21. package/dist/knowledge/normalize.js +236 -67
  22. package/dist/knowledge/outlines.js +36 -0
  23. package/dist/knowledge/platform/lint.js +385 -0
  24. package/dist/knowledge/platform/mobile.js +142 -0
  25. package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
  26. package/dist/knowledge/platform/mobileRules.js +231 -0
  27. package/dist/knowledge/platform/mobileSpacing.js +82 -0
  28. package/dist/knowledge/platform/mobileTabs.js +108 -0
  29. package/dist/knowledge/platform/nativeBoxModel.js +44 -0
  30. package/dist/knowledge/platform/profile.js +25 -0
  31. package/dist/knowledge/platform/resolve.js +122 -0
  32. package/dist/knowledge/platform/web.js +39 -0
  33. package/dist/knowledge/policy.js +18 -4
  34. package/dist/knowledge/process.js +510 -0
  35. package/dist/knowledge/reservedTables.js +79 -0
  36. package/dist/knowledge/rowFilter.js +43 -0
  37. package/dist/knowledge/rules.js +319 -21
  38. package/dist/knowledge/schemaGuard.js +11 -0
  39. package/dist/knowledge/styleResolve.js +50 -4
  40. package/dist/knowledge/styleResolveNative.js +527 -0
  41. package/dist/knowledge/themeResolve.js +3 -3
  42. package/dist/observability/trace.js +6 -3
  43. package/dist/platform/argumentAliases.js +128 -0
  44. package/dist/platform/bodyFile.js +103 -0
  45. package/dist/platform/capture/actions.js +333 -0
  46. package/dist/platform/capture/browser.js +1 -0
  47. package/dist/platform/{screenshot.js → capture/capture.js} +105 -519
  48. package/dist/platform/capture/console-log.js +122 -0
  49. package/dist/platform/capture/index.js +11 -0
  50. package/dist/platform/capture/readiness.js +103 -0
  51. package/dist/platform/capture/summary.js +77 -0
  52. package/dist/platform/capture/target.js +5 -0
  53. package/dist/platform/capture/targets/studio-mobile.js +15 -0
  54. package/dist/platform/capture/targets/studio-web.js +5 -0
  55. package/dist/platform/diagnostics.js +10 -4
  56. package/dist/platform/domSummary.js +1 -1
  57. package/dist/platform/errors.js +162 -10
  58. package/dist/platform/http.js +29 -8
  59. package/dist/platform/impersonation.js +45 -0
  60. package/dist/platform/jsonArray.js +24 -0
  61. package/dist/platform/responses.js +39 -5
  62. package/dist/platform/schemaReadMemory.js +60 -0
  63. package/dist/platform/secrets.js +17 -1
  64. package/dist/platform/strictArguments.js +68 -12
  65. package/dist/platform/tokenVault.js +187 -4
  66. package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
  67. package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
  68. package/dist/resources/examples/bpm-satinalma.json +376 -0
  69. package/dist/resources/examples/connect-data-convert-verify.json +107 -0
  70. package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
  71. package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
  72. package/dist/resources/examples/connect-data-query-params.json +50 -0
  73. package/dist/resources/examples/connect-data-table-entity.json +147 -0
  74. package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
  75. package/dist/resources/examples/mobile-app-shell-drawer.json +243 -0
  76. package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
  77. package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
  78. package/dist/resources/examples/mobile-form-screen.json +365 -0
  79. package/dist/resources/examples/mobile-list-detail-form.json +672 -0
  80. package/dist/resources/examples/page-detail-profile.json +2 -1
  81. package/dist/resources/examples/page-dynamic-form.json +3 -2
  82. package/dist/resources/examples/page-flex-sections.json +1 -1
  83. package/dist/resources/examples/page-html-embed.json +1 -1
  84. package/dist/resources/examples/workflow-basic.json +8 -2
  85. package/dist/resources/guides/access.md +796 -0
  86. package/dist/resources/guides/bpm.md +686 -0
  87. package/dist/resources/guides/case.md +153 -0
  88. package/dist/resources/guides/component.md +91 -4
  89. package/dist/resources/guides/decision.md +185 -0
  90. package/dist/resources/guides/entity.md +50 -8
  91. package/dist/resources/guides/expressions.md +14 -5
  92. package/dist/resources/guides/functions.md +1 -1
  93. package/dist/resources/guides/getting-started.md +38 -1
  94. package/dist/resources/guides/layout.md +11 -7
  95. package/dist/resources/guides/microservice.md +81 -2
  96. package/dist/resources/guides/middleware.md +44 -2
  97. package/dist/resources/guides/mobile-layout.md +193 -0
  98. package/dist/resources/guides/mobile.md +495 -0
  99. package/dist/resources/guides/page.md +454 -75
  100. package/dist/resources/guides/theme.md +8 -2
  101. package/dist/resources/guides/translation.md +1 -1
  102. package/dist/resources/guides/workflow.md +58 -1
  103. package/dist/resources/read.js +41 -0
  104. package/dist/resources/rules.js +11 -5
  105. package/dist/resources/schema-fingerprint.json +7 -5
  106. package/dist/resources/schemas/AccessExplanationDto.json +144 -0
  107. package/dist/resources/schemas/AccessMatrixDto.json +170 -0
  108. package/dist/resources/schemas/AccessPermissionDto.json +57 -0
  109. package/dist/resources/schemas/AccessPolicyDto.json +223 -0
  110. package/dist/resources/schemas/AccessResourceDto.json +86 -0
  111. package/dist/resources/schemas/AppSessionDto.json +53 -0
  112. package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
  113. package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
  114. package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
  115. package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
  116. package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
  117. package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
  118. package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
  119. package/dist/resources/schemas/CreateEntityCommand.json +118 -0
  120. package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
  121. package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
  122. package/dist/resources/schemas/CreateQueryCommand.json +4 -0
  123. package/dist/resources/schemas/CreateWorkflowCommand.json +143 -1
  124. package/dist/resources/schemas/DataJobDto.json +6 -1
  125. package/dist/resources/schemas/DecisionDto.json +302 -0
  126. package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
  127. package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
  128. package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
  129. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +21 -1
  130. package/dist/resources/schemas/MicroserviceDto.json +15 -3
  131. package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
  132. package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
  133. package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
  134. package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
  135. package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
  136. package/dist/resources/schemas/PreviewWorkflowCommand.json +22 -1
  137. package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
  138. package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
  139. package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
  140. package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
  141. package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
  142. package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
  143. package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
  144. package/dist/resources/schemas/StepDto.json +22 -1
  145. package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
  146. package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
  147. package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
  148. package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
  149. package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
  150. package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
  151. package/dist/resources/schemas/UpdateViewCommand.json +0 -2
  152. package/dist/resources/schemas/UpdateWorkflowCommand.json +143 -1
  153. package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
  154. package/dist/server.js +27 -1
  155. package/dist/tools/access.js +369 -2
  156. package/dist/tools/accessOperations.js +135 -0
  157. package/dist/tools/accessPolicies.js +170 -0
  158. package/dist/tools/accessRuntime.js +203 -0
  159. package/dist/tools/applications.js +60 -3
  160. package/dist/tools/bpm.js +343 -0
  161. package/dist/tools/bpmGovernance.js +266 -0
  162. package/dist/tools/bpmOperations.js +230 -0
  163. package/dist/tools/bpmReporting.js +154 -0
  164. package/dist/tools/bpmRouting.js +150 -0
  165. package/dist/tools/bpmRuntime.js +396 -0
  166. package/dist/tools/case.js +178 -0
  167. package/dist/tools/caseRuntime.js +89 -0
  168. package/dist/tools/common.js +7 -0
  169. package/dist/tools/components.js +118 -14
  170. package/dist/tools/connectData.js +449 -0
  171. package/dist/tools/connectors.js +193 -0
  172. package/dist/tools/datasources.js +3 -3
  173. package/dist/tools/decisions.js +206 -0
  174. package/dist/tools/dependencies.js +1 -1
  175. package/dist/tools/deployments.js +2 -2
  176. package/dist/tools/designSchemaPatch.js +34 -9
  177. package/dist/tools/dtos.js +36 -5
  178. package/dist/tools/entities.js +79 -12
  179. package/dist/tools/externalComponents.js +26 -5
  180. package/dist/tools/flows.js +122 -14
  181. package/dist/tools/folders.js +1 -1
  182. package/dist/tools/imports.js +5 -2
  183. package/dist/tools/layouts.js +50 -17
  184. package/dist/tools/merges.js +1 -1
  185. package/dist/tools/microservices.js +77 -25
  186. package/dist/tools/mobile.js +221 -0
  187. package/dist/tools/modules.js +48 -0
  188. package/dist/tools/organizationStructure.js +137 -0
  189. package/dist/tools/pages.js +465 -98
  190. package/dist/tools/platformInfo.js +27 -8
  191. package/dist/tools/profileWrite.js +92 -0
  192. package/dist/tools/projects.js +71 -9
  193. package/dist/tools/rows.js +69 -13
  194. package/dist/tools/schemaOps.js +61 -1
  195. package/dist/tools/screenWrite.js +37 -8
  196. package/dist/tools/screens.js +95 -5
  197. package/dist/tools/templates.js +26 -1
  198. package/dist/tools/translations.js +1 -1
  199. package/dist/tools/variables.js +11 -5
  200. package/dist/tools/workflows.js +10 -5
  201. package/package.json +2 -2
@@ -4,13 +4,16 @@ import { patchResult, baseVersionOf } from '../platform/patch.js';
4
4
  import fastJsonPatch from 'fast-json-patch';
5
5
  import { normalizeComponentDefaults, componentDefaultsNote, flexSizeWarnings, spacingLayerWarnings, verticalGapWarnings, withNormalization } from '../knowledge/normalize.js';
6
6
  import { fetchHost, layoutUpdateBody, schemaParam, eventsParam, modeParam, summarize, findNode } from './screens.js';
7
- import { assertScreenWriteGuards, screenCatalogNotes } from './screenWrite.js';
7
+ import { assertScreenWriteGuards, assertScreenCatalog } from './screenWrite.js';
8
8
  import { applyPropTypesToSchema, isSetPropTypeOp, setPropTypeErrors } from '../knowledge/propTypes.js';
9
9
  import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
10
10
  import { PlatformApiError } from '../platform/errors.js';
11
11
  import { handle } from '../platform/responses.js';
12
+ import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
13
+ import { screenProfileWarnings } from './profileWrite.js';
14
+ import { jsonArray } from '../platform/jsonArray.js';
12
15
  import { z } from 'zod';
13
- const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
16
+ const patchOperationsParam = jsonArray(z.array(z.record(z.unknown())).min(1))
14
17
  .describe("JSON Patch listesi (RFC 6902): { op, path, value }. Yol seçici alabilir — '/views/0/children[elementId=baslik]/props/content/data'.");
15
18
  const viewsParam = schemaParam.describe("Layout'un komponent ağacı. DİKKAT: layout'ta şema alanının adı 'schema' değil 'views'tir; ağaçta hedefli değişiklik için tam ağaç yerine layout_schema_patch micro-op'larını kullanın. İçinde MUTLAKA bir { type: 'slot' } komponenti olmalı — sayfa içeriği YALNIZ slot'un içine render edilir, slot'suz layout'a bağlanan sayfalar boş görünür. 2026-09-08'den beri views, page_schema_patch'in doğrulama duvarından geçer: tanınmayan prop adı ve geçersiz enum değeri 422 ile REDDEDİLİR (kabul edilen değer listesiyle) ve hiçbir şey kaydedilmez — eskiden sessizce kaydediliyordu (P1-2).");
16
19
  function countSlots(nodes) {
@@ -46,7 +49,13 @@ export function registerLayoutTools(server, platform) {
46
49
  description: "Layout'u komponent ağacı (views), widget'ları, akışları ve olaylarıyla getirir.",
47
50
  inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid },
48
51
  annotations: readOnly
49
- }, handle(config, ({ project, environment, applicationId, id }) => platform.tenant('GET', project, `/layouts/${applicationId}/${id}`, { environment })));
52
+ }, handle(config, async ({ project, environment, applicationId, id }) => {
53
+ const response = await platform.tenant('GET', project, `/layouts/${applicationId}/${id}`, { environment });
54
+ const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
55
+ return response && typeof response === 'object' && !Array.isArray(response) && Object.keys(typeField).length > 0
56
+ ? { ...response, ...typeField }
57
+ : response;
58
+ }));
50
59
  server.registerTool('layout_schema_get', {
51
60
  title: "Layout komponent ağacını oku",
52
61
  description: "Layout'un komponent ağacını (views) okur — page_schema_get'in layout karşılığı. Ağaçta hedefli değişiklik için layout_schema_patch (sayfa ile aynı micro-op sözlüğü, PATCH /layouts/{app}/{id}/schema). Ad/parametre/olay alanlarında ya da düz JSON Patch yolu gerektiğinde layout_patch — o araç buradaki updatedAt'i expectedUpdatedAt olarak kabul eder. Tam ağacı kendiniz yazacaksanız layout_update.",
@@ -61,20 +70,21 @@ export function registerLayoutTools(server, platform) {
61
70
  annotations: readOnly
62
71
  }, handle(config, async ({ project, environment, applicationId, id, mode, componentId }) => {
63
72
  const layout = await fetchHost(platform, 'layout', project, environment, applicationId, id);
73
+ const typeField = applicationTypeField(await resolveProfile(platform, applicationId));
64
74
  const audit = layout.audit;
65
75
  const updatedAt = audit?.updatedAt ?? audit?.createdAt;
66
76
  const views = (layout.views ?? []);
67
77
  if (mode === 'full')
68
- return { updatedAt, views };
78
+ return { updatedAt, ...typeField, views };
69
79
  if (mode === 'subtree') {
70
80
  if (!componentId)
71
81
  throw new PlatformApiError("mode='subtree' için componentId zorunludur.");
72
82
  const subtree = findNode(views, componentId);
73
83
  if (!subtree)
74
84
  throw new PlatformApiError(`Layout'ta '${componentId}' id'li bir komponent yok; layout_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
75
- return { updatedAt, views: subtree };
85
+ return { updatedAt, ...typeField, views: subtree };
76
86
  }
77
- return { updatedAt, views: views.map(node => summarize(node)) };
87
+ return { updatedAt, ...typeField, views: views.map(node => summarize(node)) };
78
88
  }));
79
89
  server.registerTool('layout_create', {
80
90
  title: 'Layout oluştur',
@@ -90,19 +100,28 @@ export function registerLayoutTools(server, platform) {
90
100
  },
91
101
  annotations: writes
92
102
  }, handle(config, async ({ project, environment, applicationId, name, views, events, parameters }) => {
93
- assertScreenWriteGuards('layout', { schema: views, events });
103
+ const resolved = await resolveProfile(platform, applicationId);
104
+ assertScreenWriteGuards('layout', { schema: views, events }, resolved.profile.platform);
94
105
  const fixed = normalizeComponentDefaults(views);
95
- const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, views);
96
- const gaps = [...verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)'), ...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(views ?? [], 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
106
+ const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, views, 'layout_create');
107
+ const gaps = [
108
+ ...(resolved.profile.layout.blocksMeaningful ? verticalGapWarnings(views ?? [], 'LAY-003 (uyarı)') : []),
109
+ ...flexSizeWarnings(views ?? [], 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
110
+ ...spacingLayerWarnings(views ?? [], 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
111
+ ...await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'layout', nodes: views ?? [] })
112
+ ];
97
113
  const response = await platform.tenant('POST', project, `/layouts/${applicationId}`, {
98
114
  environment,
99
115
  body: { name, views: views ?? [], events: events ?? [], parameters: parameters ?? null }
100
116
  });
101
117
  const drops = writeDiff({ name, views: views ?? [], events: events ?? [] }, response);
102
118
  const normalized = withWriteDiff(withNormalization(response, [...componentDefaultsNote(fixed), ...catalogNotes, ...slotWarning(views)]), drops);
103
- if (gaps.length === 0 || !normalized || typeof normalized !== 'object')
119
+ const typeField = applicationTypeField(resolved);
120
+ if (!normalized || typeof normalized !== 'object')
104
121
  return normalized;
105
- return { ...normalized, layoutWarnings: gaps };
122
+ if (gaps.length === 0)
123
+ return Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField };
124
+ return { ...normalized, ...typeField, layoutWarnings: gaps };
106
125
  }));
107
126
  server.registerTool('layout_update', {
108
127
  title: 'Layout güncelle',
@@ -120,13 +139,21 @@ export function registerLayoutTools(server, platform) {
120
139
  annotations: updates
121
140
  }, handle(config, async ({ project, environment, applicationId, id, ...changes }) => {
122
141
  const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
123
- assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events });
142
+ const resolved = overrides.views === undefined ? undefined : await resolveProfile(platform, applicationId);
143
+ assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events }, resolved?.profile.platform);
124
144
  const current = await fetchHost(platform, 'layout', project, environment, applicationId, id);
125
145
  const fixed = normalizeComponentDefaults(overrides.views);
126
- const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
146
+ const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, overrides.views, 'layout_update');
127
147
  const body = layoutUpdateBody({ ...current, applicationId }, overrides);
128
148
  await platform.tenant('PUT', project, `/layouts/${applicationId}/${id}`, { environment, body });
129
- const gaps = overrides.views === undefined ? [] : [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
149
+ const gaps = overrides.views === undefined || !resolved
150
+ ? []
151
+ : [
152
+ ...(resolved.profile.layout.blocksMeaningful ? verticalGapWarnings(body.views, 'LAY-003 (uyarı)') : []),
153
+ ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
154
+ ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
155
+ ...await screenProfileWarnings(platform, resolved, project, environment, applicationId, { kind: 'layout', nodes: body.views })
156
+ ];
130
157
  const summary = { id, changed: Object.keys(overrides), preserved: { viewNodes: body.views.length } };
131
158
  if (gaps.length > 0)
132
159
  summary.layoutWarnings = gaps;
@@ -180,9 +207,10 @@ export function registerLayoutTools(server, platform) {
180
207
  const overrides = Object.fromEntries(['name', 'views', 'events', 'parameters']
181
208
  .filter(key => patched[key] !== undefined)
182
209
  .map(key => [key, patched[key]]));
183
- assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events });
210
+ const profile = await resolveProfile(platform, applicationId);
211
+ assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events }, profile.profile.platform);
184
212
  const fixed = normalizeComponentDefaults(overrides.views);
185
- const catalogNotes = await screenCatalogNotes(platform, project, environment, applicationId, overrides.views);
213
+ const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, overrides.views, 'layout_patch');
186
214
  const body = layoutUpdateBody({ ...current, applicationId }, overrides);
187
215
  // Gövde değil FARK gönderilir: gateway normalizasyonları (komponent varsayılanları, katalog
188
216
  // kuralları, value'suz olayların elenmesi) son hâle işlendikten sonra net diff hesaplanır.
@@ -193,7 +221,12 @@ export function registerLayoutTools(server, platform) {
193
221
  body: { baseVersion: baseVersionOf(document, 'layout_get'), ops: netOps }
194
222
  });
195
223
  const written = patchResult(saved, finalDocument);
196
- const gaps = [...verticalGapWarnings(body.views, 'LAY-003 (uyarı)'), ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'), ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)')];
224
+ const gaps = [
225
+ ...(profile.profile.layout.blocksMeaningful ? verticalGapWarnings(body.views, 'LAY-003 (uyarı)') : []),
226
+ ...flexSizeWarnings(body.views, 'LAY-007 (uyarı)', 'LAY-008 (uyarı)'),
227
+ ...spacingLayerWarnings(body.views, 'LAY-010 (uyarı)', 'LAY-011 (uyarı)'),
228
+ ...await screenProfileWarnings(platform, profile, project, environment, applicationId, { kind: 'layout', nodes: body.views })
229
+ ];
197
230
  const summary = {
198
231
  id,
199
232
  applied: patchOps.length,
@@ -37,7 +37,7 @@ export function registerMergeTools(server, platform) {
37
37
  const config = platform.config;
38
38
  server.registerTool('merge_plan', {
39
39
  title: 'Merge planı oluştur',
40
- description: "Bir dalı ebeveynine (development) merge etmek için üç yönlü diff planı ister. Hedef her zaman dalın ebeveynidir — production'a merge eden bir yol YOKTUR. title ZORUNLUDUR: isteğin adıdır ve geçmişte o adla durur. Asenkron: yanıt 202 döner, plan hemen dolu değildir. Projede açık bir plan varsa 400 döner; önce onu uygulayın ya da merge_discard ile bırakın.",
40
+ description: "Bir dalı ebeveynine (development) merge etmek için üç yönlü diff planı ister. Hedef her zaman dalın ebeveynidir — production'a merge eden bir yol YOKTUR. title ZORUNLUDUR: isteğin adıdır ve geçmişte o adla durur. Asenkron: yanıt 202 döner, plan hemen dolu değildir. Projede açık bir plan varsa 400 döner; önce onu uygulayın ya da merge_discard ile bırakın. Süreç tanımları ve iş takvimleri planda modül olarak görünür (fark versions[…].model.nodes[<düğüm id>] yolunda); örnekler, görevler, defter ve uyarlamalar merge edilmez.",
41
41
  inputSchema: { projectId: guid, sourceEnvironmentId: guid, title: mergeTitle, description: mergeDescription },
42
42
  annotations: writes
43
43
  }, handle(config, async ({ projectId, sourceEnvironmentId, title, description }) => {
@@ -1,19 +1,24 @@
1
- import { guid, appRef, withResolved, projectParam, environmentParam, definitionParam, languageParam, pagingParams, readOnly, writes, updates, destructive, microOps } from './common.js';
1
+ import { guid, appRef, withResolved, projectParam, environmentParam, definitionParam, languageParam, pagingParams, asUserIdParam, readOnly, writes, updates, destructive, microOps } from './common.js';
2
2
  import { languageHeader } from '../knowledge/languageCodes.js';
3
3
  import { hydrateMicroserviceRefs } from '../knowledge/hydrate.js';
4
4
  import { RefResolver } from '../platform/refs.js';
5
5
  import { normalizeMicroserviceDefinition, normalizeMicroservicePatchOps, withNormalization } from '../knowledge/normalize.js';
6
6
  import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
7
7
  import { microserviceOutline, workflowOutline, resolvePart, resolvePatchOperations } from '../knowledge/outlines.js';
8
+ import { unresolvedParameterAliases, parameterAliasWarning } from '../knowledge/microserviceParameters.js';
8
9
  import { patchResult, baseVersionOf } from '../platform/patch.js';
9
- import { policyRules } from '../knowledge/rules.js';
10
+ import { filterOperatorError, policyRules } from '../knowledge/rules.js';
10
11
  import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
11
- import { identityHeaders } from '../platform/tokenVault.js';
12
+ import { identityHeaders, resolveIdentity } from '../platform/tokenVault.js';
13
+ import { impersonationHeaders } from '../platform/impersonation.js';
14
+ import { accessDecisionOf, legacyRoleListFindings, projectAccessContext } from '../knowledge/access.js';
12
15
  import { PlatformApiError } from '../platform/errors.js';
13
16
  import { existingMatch, ifExistsParam, listAll, platformRename, withOutcome, withRename } from '../platform/ifExists.js';
14
17
  import { outlineWrite, recordWriteViewParam } from '../platform/writeView.js';
15
18
  import { handle } from '../platform/responses.js';
19
+ import { headersForMode } from '../platform/http.js';
16
20
  import fastJsonPatch from 'fast-json-patch';
21
+ import { jsonArray } from '../platform/jsonArray.js';
17
22
  import { z } from 'zod';
18
23
  import { constraintSummary } from '../knowledge/backendConstraints.js';
19
24
  import { unmappedMembers, introducedUnmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
@@ -66,9 +71,9 @@ async function queryScopeFindings(platform, project, environment, definition) {
66
71
  }]
67
72
  : [];
68
73
  }
69
- const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
74
+ const patchOperationsParam = jsonArray(z.array(z.record(z.unknown())).min(1))
70
75
  .describe("RFC 6902 JSON Patch işlemleri; path'leri view:'outline'/'part' ile keşfedin. Dizi öğelerinde indeks yerine seçici kullanılabilir: '/sourceParameters/joins[alias=customers]/path' — seçici gövde platforma gitmeden indekse çevrilir, çözülemezse istek hiç gönderilmez.");
71
- const asUserParam = z.string().min(1).optional().describe("Uygulama üyesinin kimliği: istek gateway'in servis jetonu yerine bu kimlikle gider. Jeton HEM Authorization başlığında HEM NG_AccessToken çerezinde taşınır, böylece jetonu ÇEREZDEN okuyan middleware de geçer — uca middleware bağlandığı anda bu alan olmadan çağrı 401 döner (09-05 koşusu S-21). İKİ ŞEKİL kabul edilir: (1) ham JWT, (2) `tok_…` referansı (giriş ucunu çağırdığınızda yanıtın tokenRefs alanında döner, 15 dk geçerli). anonymous ile birlikte verilemez.");
76
+ const asUserParam = z.string().min(1).optional().describe("Uygulama üyesinin kimliği: istek gateway'in servis jetonu yerine bu kimlikle gider. Jeton HEM Authorization başlığında HEM NG_AccessToken çerezinde taşınır, böylece jetonu ÇEREZDEN okuyan middleware de geçer — uca middleware bağlandığı anda bu alan olmadan çağrı 401 döner (09-05 koşusu S-21). KOMPLEKS PROJEDE referans access_session_create'ten gelir: jeton Bearer olarak gider, platform Bearer'lı isteğe CSRF sormaz (ZY_App_ çerezi kurulmaz — çerezle gelen durum değiştiren istek X-ZY-CSRF ister); microservice_preview bir Studio ucudur ve uygulama jetonuyla sınanamaz, uygulama kullanıcısı olarak microservice_execute / microservice_debug kullanın. ÜÇ ŞEKİL kabul edilir: (1) TERCİH EDİLEN `test:<ad>` — gateway kök .env'deki MCP_TEST_USER_<AD> satırıyla giriş ucunu kendisi çağırır, jetonu kasadan yeniden kullanır (satırı insan yazar), (2) `tok_…` referansı (giriş ucunu çağırdığınızda yanıtın tokenRefs alanında döner; ömür MCP_TOKEN_TTL_MINUTES, MCP_STATE_DIR varsa gateway yeniden başlayınca da çözülür), (3) ham JWT. anonymous ile birlikte verilemez.");
72
77
  const cookiesParam = z.record(z.string()).optional().describe("İsteğe eklenecek çerezler (ad → değer): uygulamanın KENDİ oturum çerezi buraya verilir (ör. { CORE_Token: '<jwt>' }). asUser verildiğinde gateway NG_AccessToken çerezini zaten kendisi kurar; bu alan yalnız uygulama başka bir çerez adı okuyorsa gerekir. anonymous ile birlikte verilemez.");
73
78
  const executeInput = {
74
79
  project: projectParam,
@@ -85,6 +90,7 @@ const executeInput = {
85
90
  headers: z.record(z.string()).optional().describe("İsteğe eklenecek header'lar. 'Authorization' verirseniz gateway KENDİ token'ını KOYMAZ — istek tamamen sizin kimliğinizle gider (middleware'li uçları üye token'ıyla test etmenin yolu budur)"),
86
91
  anonymous: z.boolean().optional().describe("true: istek HİÇBİR Authorization header'ı olmadan gider — 'korumalı uç tokensız ne döner?' sorusunun cevabı budur (korumalı uçtan 401, anonim uçtan 200 beklenir). Gateway normalde her çağrıya kendi token'ını koyduğu için bu bayrak olmadan anonim erişim testi yapılamaz."),
87
92
  asUser: asUserParam,
93
+ asUserId: asUserIdParam,
88
94
  cookies: cookiesParam,
89
95
  language: languageParam,
90
96
  expectFailure: z.boolean().optional().describe("true: bu çağrıda bir hata dönmesi BEKLENİYOR (kasıtlı negatif test — yanlış parola, yetkisiz erişim, geçersiz gövde). Yanıt yine httpStatus'u/hatayı taşır, ama ölçüm aracı bu satırı plansız hata saymaz (page_screenshot'taki expectFailure deseniyle aynı).")
@@ -99,6 +105,9 @@ function runIdentity(anonymous, asUser, cookies, headers) {
99
105
  }
100
106
  return identityHeaders(asUser, cookies, headers);
101
107
  }
108
+ function hasAuthorization(headers) {
109
+ return Object.keys(headers ?? {}).some(name => name.toLowerCase() === 'authorization');
110
+ }
102
111
  /**
103
112
  * Debug çıktısındaki `executionItems` ucun kendi adımlarıdır; middleware AYRI bir workflow olarak
104
113
  * ucun ÖNÜNDE koşar ve ağaçta hiç görünmez — 09-04 koşusunda sessizce açık kalan şirket kapısı
@@ -189,17 +198,27 @@ function unboundQueryParameters(value, into = []) {
189
198
  unboundQueryParameters(entry, into);
190
199
  return into;
191
200
  }
201
+ /** Çağrının dinamik filtresi (`filters`, sorguda ya da gövdede) bilinen işleçlerle mi — değilse istek gitmez (g-03). */
202
+ function assertFilterOperators(tool, bound) {
203
+ const bodyFilters = bound.body && !Array.isArray(bound.body) ? bound.body.filters : undefined;
204
+ const error = filterOperatorError(tool, bound.query?.filters) ?? filterOperatorError(tool, bodyFilters);
205
+ if (error)
206
+ throw new PlatformApiError(error, 400);
207
+ }
192
208
  function executePath(prefix, application, microservice, routeParams) {
193
209
  const routeSuffix = routeParams && routeParams.length > 0
194
210
  ? `/${routeParams.map(encodeURIComponent).join('/')}`
195
211
  : '';
196
212
  return `/${prefix}/${application}/${microservice}${routeSuffix}`;
197
213
  }
198
- /** Slug application_update ile değişebildiği için önbellek kısa ömürlüdür (yalnız aynı turdaki tekrar okumaları ucuzlatır). */
214
+ /**
215
+ * Slug application_update ile değişebildiği için önbellek kısa ömürlüdür (yalnız aynı turdaki tekrar okumaları ucuzlatır);
216
+ * runtime yolu 404 dönen çağıran `fresh: true` ile önbelleği atlar. Org API'nin 404 dışı hatası (yetki, ağ) çağırana gider.
217
+ */
199
218
  const APPLICATION_SLUG_TTL_MS = 60_000;
200
219
  const applicationSlugCache = new Map();
201
- async function applicationSlug(platform, applicationId) {
202
- const cached = applicationSlugCache.get(applicationId);
220
+ export async function applicationSlug(platform, applicationId, options = {}) {
221
+ const cached = options.fresh ? undefined : applicationSlugCache.get(applicationId);
203
222
  if (cached && cached.expiresAt > Date.now())
204
223
  return cached.slug;
205
224
  try {
@@ -210,8 +229,10 @@ async function applicationSlug(platform, applicationId) {
210
229
  applicationSlugCache.set(applicationId, { slug, expiresAt: Date.now() + APPLICATION_SLUG_TTL_MS });
211
230
  return slug;
212
231
  }
213
- catch {
214
- return undefined;
232
+ catch (error) {
233
+ if (error instanceof PlatformApiError && error.status === 404)
234
+ return undefined;
235
+ throw error;
215
236
  }
216
237
  }
217
238
  /**
@@ -241,6 +262,12 @@ async function resolveRuntimeUrl(platform, project, environment, applicationId,
241
262
  }
242
263
  export function registerMicroserviceTools(server, platform) {
243
264
  const config = platform.config;
265
+ server.registerTool('microservice_system_operation_list', {
266
+ title: 'Sistem operasyonlarını listele',
267
+ description: "Bir microservice'in systemOperation alanına konabilecek hazır platform operasyonlarının kataloğunu döner (GET /{project}/microservices/system-operations): operasyon adı, karar motorunun soracağı izin, kaynak tipi, beklediği parametreler ve açıklaması. Sistem operasyonlu uç kendi adımlarını yazmaz — platformun hazır davranışını (ör. satır sahibini değiştirme, maskeli alan açma, uygulama girişi) uygulamanın kendi ucu gibi yayınlar. Her satırın 'guard' alanı korumayı söyler: 'permission' → oturum + buradaki permission karara bağlanır; permission BOŞ olan 'permission' satırında izni işlemin çağırdığı servis verir (ADR-0004): süreç çalışma zamanı işlemleri (process.* — görev kutusu ve eylemleri, process.task.sign dahil; örnek, vekalet, devir, bildirim, tercih, rapor) kişiye ve kayda göre karar verir, REST bpm/* ucuyla aynı görünürlüğü ve aynı alan adlarını döner; yalnız process.override.* process.manage ister; 'session' → operasyon izin sormaz, yalnız oturum ister (ör. access.session.get: oturumdaki kullanıcı; uç anonim değilse microservice.execute kararı yine uygulanır); 'anonymous' → oturum da izin de sorulmaz (access.session.create: giriş; girdi yalnız gövdeden okunur, çerezler yalnız application/json istekte yazılır, gövdede jeton dönmez) ve uç isAnonymous:true olmak zorundadır, önizlemede çalışmaz. Süreç ekranı process.* işlemleriyle kurulur, Page.bpm ile değil (BPM-022); kod adımından platform ucuna ham fetch yerine bu uçlar kullanılır (FLOW-028). Katalog sabittir, projeye göre değişmez.",
268
+ inputSchema: { project: projectParam, environment: environmentParam },
269
+ annotations: readOnly
270
+ }, handle(config, ({ project, environment }) => platform.tenant('GET', project, '/microservices/system-operations', { environment })));
244
271
  server.registerTool('microservice_list', {
245
272
  title: 'Microservice listele',
246
273
  description: 'Application altındaki microservice\'leri listeler.',
@@ -264,7 +291,7 @@ export function registerMicroserviceTools(server, platform) {
264
291
  }));
265
292
  server.registerTool('microservice_get', {
266
293
  title: 'Microservice getir',
267
- description: "Microservice tanımını getirir. Büyük tanımda önce view:'outline' (kaynak/alan özeti), sonra view:'part' ile tek parça çekin.",
294
+ description: "Microservice tanımını getirir. Büyük tanımda önce view:'outline' (kaynak/alan özeti), sonra view:'part' ile tek parça çekin. parameterScheme[].name ÇAĞRI PARAMETRESİNİN adıdır ve tablo kaynaklı uçta kök filtre için nitelenmemiş alan slug'ıdır ('id'); nitelikli bir ad ('kredi.id') yalnız JOIN alias'ıyla meşrudur — join değilse o filtre runtime'da hiç çözülmez ve yanıt MS-023 uyarısını taşır (TB-119).",
268
295
  inputSchema: {
269
296
  project: projectParam,
270
297
  environment: environmentParam,
@@ -285,9 +312,16 @@ export function registerMicroserviceTools(server, platform) {
285
312
  return withResolved({ part, value: resolvePart(definition, part) }, resolver.resolved);
286
313
  }
287
314
  const resolvedUrl = await resolveRuntimeUrl(platform, project, environment, resolvedApplicationId, definition);
315
+ // MS-023 — entity slug'ı YALNIZ kusur varken okunur: sağlam uçta fazladan istek atılmaz.
316
+ const unresolved = unresolvedParameterAliases(definition);
317
+ const entitySlug = unresolved.length === 0 || typeof definition.entityId !== 'string'
318
+ ? undefined
319
+ : await resolver.entity(definition.entityId).then(entity => entity.slug).catch(() => undefined);
320
+ const warning = parameterAliasWarning(unresolved, entitySlug);
321
+ const parameterWarnings = warning === undefined ? {} : { parameterWarnings: [warning] };
288
322
  if (view === 'outline')
289
- return withResolved({ ...microserviceOutline(definition), resolvedUrl }, resolver.resolved);
290
- return withResolved({ ...payload, resolvedUrl }, resolver.resolved);
323
+ return withResolved({ ...microserviceOutline(definition), resolvedUrl, ...parameterWarnings }, resolver.resolved);
324
+ return withResolved({ ...payload, resolvedUrl, ...parameterWarnings }, resolver.resolved);
291
325
  }));
292
326
  server.registerTool('microservice_create', {
293
327
  title: 'Microservice oluştur',
@@ -321,7 +355,8 @@ export function registerMicroserviceTools(server, platform) {
321
355
  ...(existing === undefined
322
356
  ? createFindings
323
357
  : enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'update' }, policyOverride))),
324
- ...scopeFindings
358
+ ...scopeFindings,
359
+ ...await legacyRoleListFindings(platform, project, environment, definition, 'microservice', policyOverride)
325
360
  ];
326
361
  const drift = unmappedMembers(existing === undefined ? 'CreateMicroserviceCommand' : 'UpdateMicroserviceCommand', definition);
327
362
  const payload = existing === undefined
@@ -343,7 +378,8 @@ export function registerMicroserviceTools(server, platform) {
343
378
  const normalized = normalizeMicroserviceDefinition(definition);
344
379
  const findings = [
345
380
  ...enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'update' }, policyOverride)),
346
- ...await queryScopeFindings(platform, project, environment, definition)
381
+ ...await queryScopeFindings(platform, project, environment, definition),
382
+ ...await legacyRoleListFindings(platform, project, environment, definition, 'microservice', policyOverride)
347
383
  ];
348
384
  const drift = unmappedMembers('UpdateMicroserviceCommand', definition);
349
385
  const payload = await platform.tenant('PUT', project, `/microservices/${resolvedApplicationId}/${id}`, { environment, body: definition });
@@ -406,26 +442,41 @@ export function registerMicroserviceTools(server, platform) {
406
442
  }, handle(config, ({ project, environment, id }) => platform.tenant('POST', project, `/microservices/${id}/clone`, { environment })));
407
443
  server.registerTool('microservice_execute', {
408
444
  title: 'Microservice çalıştır',
409
- description: "Kayıtlı microservice'i gerçek runtime yolundan çalıştırır (POST /{project}/m/{application}/{microservice SLUG'ı}). Dönen httpStatus microservice'in kendi sonucudur (hata da geçerli çıktıdır); beklenmedik sonuçta microservice_debug ile adım adım inceleyin. Anonim erişim testi için anonymous: true. Yanıt 'headers' alanında platformun döndüğü header'ları taşır (küçük harf; ör. headers['total-count'] — sayfalı tablo akışlarının okuduğu sözleşme; set-cookie/authorization elenir).",
410
- inputSchema: executeInput,
445
+ description: "Kayıtlı microservice'i gerçek runtime yolundan çalıştırır (POST /{project}/m/{application}/{microservice SLUG'ı}). Dönen httpStatus microservice'in kendi sonucudur (hata da geçerli çıktıdır); beklenmedik sonuçta microservice_debug ile adım adım inceleyin. Anonim erişim testi için anonymous: true. KOMPLEKS PROJEDE gateway kimliği uygulama öznesi değildir (enforce'ta 401 AppSessionRequired): kullanıcı gözüyle çalıştırmak için asUserId (parolasız, users satır id'si; yalnız development/dal — production'da istek gönderilmez) ya da oturum açılmış üye için asUser. Yanıt 'headers' alanı VARSAYILAN OLARAK yalnız iş başlıklarını taşır (küçük harf): content-type, content-length, content-disposition, location, total-count (sayfalı tablo akışlarının okuduğu sözleşme), x-request-id, traceparent. Ucun response adımında kendi yazdığı başlıkları ya da sunucu başlıklarını görmek için includeHeaders:'all'; hiç başlık istemiyorsanız 'none'. set-cookie/authorization her kipte elenir.",
446
+ inputSchema: {
447
+ ...executeInput,
448
+ includeHeaders: z.enum(['none', 'essential', 'all']).optional().describe("Yanıttaki 'headers' alanı. essential (varsayılan): content-type, content-length, content-disposition, location, total-count, x-request-id, traceparent. all: platformun döndüğü bütün başlıklar (gizliler hariç) — ucun kendi yazdığı başlığı doğrulamak için. none: başlık yok")
449
+ },
411
450
  annotations: writes
412
- }, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, cookies, language }) => {
451
+ }, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, asUserId, cookies, language, includeHeaders }) => {
413
452
  const notes = [];
414
- const requestHeaders = { ...runIdentity(anonymous, asUser, cookies, headers), ...languageHeader(language, notes) };
453
+ const eye = await impersonationHeaders(platform, 'microservice_execute', project, environment, { asUserId, asUser, anonymous, ownAuthorization: hasAuthorization(headers) }, { runtime: true });
454
+ const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
455
+ const requestHeaders = { ...runIdentity(anonymous, member, cookies, headers), ...eye, ...languageHeader(language, notes) };
415
456
  const bound = bindCallParameters(method, parameters, query, body, notes);
416
- return withNormalization(await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true }), notes);
457
+ assertFilterOperators('microservice_execute', bound);
458
+ const raw = await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true });
459
+ return withNormalization({ ...raw, headers: headersForMode(raw.headers, includeHeaders ?? 'essential') }, notes);
417
460
  }));
418
461
  server.registerTool('microservice_debug', {
419
462
  title: 'Microservice debug',
420
- description: "Microservice'i debug modunda çalıştırır; yanıtta adım adım executionItems ağacı döner (her adımın girdi/çıktısı). executionItems YALNIZ ucun kendi adımlarını kapsar — ucun ÖNÜNDE koşan middleware'ler ayrıca 'middlewares' alanında adım özetiyle listelenir (09-04 Y-04). Değeri NULL bağlanan query parametreleri 'unboundParameters' alanında adıyla listelenir (ağaçta bu satırların value anahtarı görünmez). Preview semantiğiyle koşar: tek transaction + sonunda rollback, kalıcı satır yazmaz. httpStatus akışın kendi sonucudur (4xx/5xx'te runtimeErrors eklenir); ağaç düğümlerinde hata bayrağı YOKTUR — patlayan adımın output'u boştur, hata gövdesi hata dalındaki response adımının çıktısındadır. Zamanlanmış (schedule-job) uçlar da slug yoluyla debug edilir (method: 'POST', parametresiz): job geçmişi hatayı yalnız mesaj olarak yazar, adım adım girdi/çıktı burada görülür (platform_guide('microservice') §Zamanlanmış işin gerçek sonucunu görmek).",
463
+ description: "Microservice'i debug modunda çalıştırır; yanıtta adım adım executionItems ağacı döner (her adımın girdi/çıktısı). executionItems YALNIZ ucun kendi adımlarını kapsar — ucun ÖNÜNDE koşan middleware'ler ayrıca 'middlewares' alanında adım özetiyle listelenir (09-04 Y-04). Değeri NULL bağlanan query parametreleri 'unboundParameters' alanında adıyla listelenir (ağaçta bu satırların value anahtarı görünmez). Preview semantiğiyle koşar: tek transaction + sonunda rollback, kalıcı satır yazmaz. httpStatus akışın kendi sonucudur (4xx/5xx'te runtimeErrors eklenir); ağaç düğümlerinde hata bayrağı YOKTUR — patlayan adımın output'u boştur, hata gövdesi hata dalındaki response adımının çıktısındadır. Zamanlanmış (schedule-job) uçlar da slug yoluyla debug edilir (method: 'POST', parametresiz): job geçmişi hatayı yalnız mesaj olarak yazar, adım adım girdi/çıktı burada görülür (platform_guide('microservice') §Zamanlanmış işin gerçek sonucunu görmek). Kompleks projede kullanıcı gözü asUserId (parolasız) ya da asUser ile verilir ve yanıta 'accessDecision' eklenir: { decision: allow | deny | not-enforced | unknown, mode, code?, permission?, reason, next } — 401/403'ün erişim kararından mı geldiğini ve sıradaki adımı söyler.",
421
464
  inputSchema: executeInput,
422
465
  annotations: writes
423
- }, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, cookies, language }) => {
466
+ }, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, asUserId, cookies, language }) => {
424
467
  const notes = [];
425
- const requestHeaders = { ...runIdentity(anonymous, asUser, cookies, headers), ...languageHeader(language, notes) };
468
+ const eye = await impersonationHeaders(platform, 'microservice_debug', project, environment, { asUserId, asUser, anonymous, ownAuthorization: hasAuthorization(headers) }, { runtime: true });
469
+ const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
470
+ const requestHeaders = { ...runIdentity(anonymous, member, cookies, headers), ...eye, ...languageHeader(language, notes) };
426
471
  const bound = bindCallParameters(method, parameters, query, body, notes);
472
+ assertFilterOperators('microservice_debug', bound);
427
473
  const payload = await platform.tenantRaw(method, project, executePath('debug', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true });
428
474
  const unbound = unboundQueryParameters(payload.body);
475
+ const chain = await middlewareChain(platform, project, environment, application, microservice, payload.httpStatus);
476
+ // Karar yanıtta ayrı alan olarak gelmez: kompleks projede hata kodundan ve ortamın modundan türetilir
477
+ // (g-31). Proje okuması zincirden SONRA yapılır; basit projede alan hiç eklenmez.
478
+ const errorBody = (payload.body && typeof payload.body === 'object' ? payload.body : {});
479
+ const decision = accessDecisionOf(await projectAccessContext(platform, project, environment), payload.httpStatus, errorBody.code, errorBody.message);
429
480
  return withNormalization({
430
481
  ...payload,
431
482
  ...(unbound.length > 0
@@ -434,7 +485,8 @@ export function registerMicroserviceTools(server, platform) {
434
485
  unboundParametersNote: "Bu query parametreleri NULL bağlandı: çağrıda yoklar ya da değerleri boş string'di (platform boş string'i NULL'a çevirir). Parametreyi 'parameters' (ya da GET'te 'query', POST/PUT'ta 'body') ile verin; ad sorgunun parametre adıyla birebir aynı olmalı."
435
486
  }
436
487
  : {}),
437
- ...await middlewareChain(platform, project, environment, application, microservice, payload.httpStatus)
488
+ ...chain,
489
+ ...(decision ? { accessDecision: decision } : {})
438
490
  }, notes);
439
491
  }));
440
492
  server.registerTool('microservice_preview', {
@@ -474,7 +526,7 @@ export function registerMicroserviceTools(server, platform) {
474
526
  const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'microservice', definition, { operation: 'preview' }, policyOverride));
475
527
  // Önizleme de ucun middleware'lerini koşturur: kimlik verilmezse korumalı uç 401 döner ve kuru
476
528
  // koşu aracı tam gerektiği anda kullanılamaz hâle gelir (09-05 koşusu S-21).
477
- const payload = await platform.tenantRaw('POST', project, `/microservices/${resolvedApplicationId}/preview`, { environment, body: definition, headers: { ...identityHeaders(asUser, cookies, undefined), ...languageHeader(language, normalized) }, mirrorAuthorizationCookie: true });
529
+ const payload = await platform.tenantRaw('POST', project, `/microservices/${resolvedApplicationId}/preview`, { environment, body: definition, headers: { ...identityHeaders(await resolveIdentity(asUser, platform, project, environment), cookies, undefined), ...languageHeader(language, normalized) }, mirrorAuthorizationCookie: true });
478
530
  return attachFindings(withResolved(withNormalization(payload, normalized), [...resolver.resolved, ...hydrated]), findings);
479
531
  }));
480
532
  server.registerTool('microservice_job_history', {