@appaflytech/zeyrek-ai-mcp 0.0.5 → 0.0.7

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 (145) hide show
  1. package/.env.example +2 -2
  2. package/.env.production.example +9 -1
  3. package/README.md +14 -38
  4. package/dist/config.js +36 -3
  5. package/dist/knowledge/backendConstraints.js +982 -0
  6. package/dist/knowledge/componentCatalog.js +549 -92
  7. package/dist/knowledge/componentEvents.js +120 -25
  8. package/dist/knowledge/coverage.js +43 -0
  9. package/dist/knowledge/flowGuards.js +30 -0
  10. package/dist/knowledge/functionGuards.js +134 -0
  11. package/dist/knowledge/guideSections.js +49 -0
  12. package/dist/knowledge/hydrate.js +97 -0
  13. package/dist/knowledge/languageCodes.js +36 -0
  14. package/dist/knowledge/languages.js +105 -0
  15. package/dist/knowledge/layoutSummary.js +10 -0
  16. package/dist/knowledge/localization.js +51 -0
  17. package/dist/knowledge/normalize.js +500 -16
  18. package/dist/knowledge/outlines.js +167 -12
  19. package/dist/knowledge/policy.js +14 -5
  20. package/dist/knowledge/propTypes.js +80 -0
  21. package/dist/knowledge/rules.js +961 -44
  22. package/dist/knowledge/schemaGuard.js +199 -0
  23. package/dist/knowledge/styleResolve.js +63 -4
  24. package/dist/observability/trace.js +27 -3
  25. package/dist/platform/csv.js +123 -0
  26. package/dist/platform/errors.js +107 -6
  27. package/dist/platform/http.js +34 -4
  28. package/dist/platform/patch.js +27 -0
  29. package/dist/platform/refs.js +104 -0
  30. package/dist/platform/responses.js +25 -6
  31. package/dist/platform/screenshot.js +544 -16
  32. package/dist/platform/secrets.js +40 -0
  33. package/dist/platform/tokenVault.js +89 -0
  34. package/dist/platform/writeDiff.js +281 -0
  35. package/dist/resources/examples/flow-call-function.json +66 -0
  36. package/dist/resources/examples/function-total.json +70 -0
  37. package/dist/resources/examples/layout-app-shell.json +1 -1
  38. package/dist/resources/examples/microfunction-node-sum.json +56 -0
  39. package/dist/resources/examples/microservice-table-list.json +2 -4
  40. package/dist/resources/examples/page-dynamic-form.json +3 -3
  41. package/dist/resources/examples/page-flex-sections.json +99 -0
  42. package/dist/resources/examples/page-sign-in.json +4 -3
  43. package/dist/resources/guides/component.md +81 -8
  44. package/dist/resources/guides/entity.md +712 -20
  45. package/dist/resources/guides/expressions.md +47 -2
  46. package/dist/resources/guides/external-component.md +9 -0
  47. package/dist/resources/guides/functions.md +262 -0
  48. package/dist/resources/guides/getting-started.md +82 -7
  49. package/dist/resources/guides/layout.md +53 -10
  50. package/dist/resources/guides/merge.md +231 -0
  51. package/dist/resources/guides/microfunction.md +166 -0
  52. package/dist/resources/guides/microservice.md +287 -15
  53. package/dist/resources/guides/middleware.md +89 -1
  54. package/dist/resources/guides/modify-existing.md +23 -0
  55. package/dist/resources/guides/page.md +402 -49
  56. package/dist/resources/guides/translation.md +150 -8
  57. package/dist/resources/guides/view.md +16 -1
  58. package/dist/resources/guides/workflow.md +199 -28
  59. package/dist/resources/knowledge.js +5 -0
  60. package/dist/resources/rules.js +27 -0
  61. package/dist/resources/schema-fingerprint.json +8 -0
  62. package/dist/resources/schemas/AnalyzeImportQuery.json +51 -0
  63. package/dist/resources/schemas/ApplyMergePlanCommand.json +19 -0
  64. package/dist/resources/schemas/CreateBulkRowCommand.json +36 -0
  65. package/dist/resources/schemas/CreateDatasourceCommand.json +15 -6
  66. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +19 -8
  67. package/dist/resources/schemas/CreateEntityCommand.json +52 -12
  68. package/dist/resources/schemas/CreateEnvironmentCommand.json +31 -0
  69. package/dist/resources/schemas/CreateGlobalFunctionCommand.json +95 -0
  70. package/dist/resources/schemas/CreateImportPlanCommand.json +278 -0
  71. package/dist/resources/schemas/CreateMergePlanCommand.json +34 -0
  72. package/dist/resources/schemas/CreateMicrofunctionCommand.json +117 -0
  73. package/dist/resources/schemas/CreateMicrofunctionRuntimeCommand.json +51 -0
  74. package/dist/resources/schemas/CreateMicroserviceCommand.json +145 -3
  75. package/dist/resources/schemas/CreateQueryCommand.json +56 -3
  76. package/dist/resources/schemas/CreateRowCommand.json +38 -0
  77. package/dist/resources/schemas/CreateViewCommand.json +13 -10
  78. package/dist/resources/schemas/CreateWorkflowCommand.json +106 -14
  79. package/dist/resources/schemas/DataJobDto.json +267 -0
  80. package/dist/resources/schemas/ExportRowsCommand.json +172 -0
  81. package/dist/resources/schemas/Filter.json +3 -1
  82. package/dist/resources/schemas/GetRowsQuery.json +6 -1
  83. package/dist/resources/schemas/GlobalFunctionDto.json +89 -0
  84. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +249 -0
  85. package/dist/resources/schemas/ImportAnalysisDto.json +401 -0
  86. package/dist/resources/schemas/ImportPlanDto.json +260 -0
  87. package/dist/resources/schemas/ImportSampleDto.json +90 -0
  88. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +25 -0
  89. package/dist/resources/schemas/PreviewMicroserviceCommand.json +135 -1
  90. package/dist/resources/schemas/PreviewQueryCommand.json +5 -2
  91. package/dist/resources/schemas/PreviewWorkflowCommand.json +45 -9
  92. package/dist/resources/schemas/ResolveMergeConflictsCommand.json +58 -0
  93. package/dist/resources/schemas/RevertMergePlanCommand.json +19 -0
  94. package/dist/resources/schemas/RunImportCommand.json +62 -0
  95. package/dist/resources/schemas/StepDto.json +34 -3
  96. package/dist/resources/schemas/UpdateBulkRowCommand.json +36 -0
  97. package/dist/resources/schemas/UpdateDatasourceCommand.json +15 -6
  98. package/dist/resources/schemas/UpdateEntityCommand.json +52 -11
  99. package/dist/resources/schemas/UpdateGlobalFunctionCommand.json +66 -0
  100. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +121 -0
  101. package/dist/resources/schemas/UpdateMicrofunctionRuntimeCommand.json +54 -0
  102. package/dist/resources/schemas/UpdateMicroserviceCommand.json +147 -4
  103. package/dist/resources/schemas/UpdateQueryCommand.json +13 -3
  104. package/dist/resources/schemas/UpdateRowCommand.json +40 -0
  105. package/dist/resources/schemas/UpdateViewCommand.json +15 -10
  106. package/dist/resources/schemas/UpdateWorkflowCommand.json +106 -14
  107. package/dist/server.js +17 -1
  108. package/dist/tools/access.js +3 -3
  109. package/dist/tools/activities.js +40 -0
  110. package/dist/tools/applications.js +1 -1
  111. package/dist/tools/automations.js +76 -34
  112. package/dist/tools/common.js +55 -0
  113. package/dist/tools/components.js +16 -7
  114. package/dist/tools/datasources.js +83 -21
  115. package/dist/tools/dependencies.js +1 -1
  116. package/dist/tools/deployments.js +34 -2
  117. package/dist/tools/dtos.js +25 -12
  118. package/dist/tools/entities.js +113 -28
  119. package/dist/tools/environments.js +52 -0
  120. package/dist/tools/externalComponents.js +9 -3
  121. package/dist/tools/files.js +57 -42
  122. package/dist/tools/flows.js +205 -13
  123. package/dist/tools/folders.js +7 -2
  124. package/dist/tools/functions.js +331 -0
  125. package/dist/tools/globalFunctions.js +63 -0
  126. package/dist/tools/importSchemas.js +79 -0
  127. package/dist/tools/imports.js +241 -0
  128. package/dist/tools/layouts.js +103 -29
  129. package/dist/tools/merges.js +113 -0
  130. package/dist/tools/microfunctions.js +155 -0
  131. package/dist/tools/microservices.js +270 -40
  132. package/dist/tools/organizations.js +5 -6
  133. package/dist/tools/pages.js +421 -104
  134. package/dist/tools/platformInfo.js +155 -11
  135. package/dist/tools/projects.js +183 -33
  136. package/dist/tools/queries.js +32 -10
  137. package/dist/tools/revisions.js +94 -0
  138. package/dist/tools/rows.js +484 -50
  139. package/dist/tools/screenWrite.js +68 -0
  140. package/dist/tools/screens.js +28 -0
  141. package/dist/tools/templates.js +32 -5
  142. package/dist/tools/translations.js +24 -8
  143. package/dist/tools/views.js +20 -10
  144. package/dist/tools/workflows.js +56 -21
  145. package/package.json +7 -2
@@ -0,0 +1,68 @@
1
+ import { invalidScreenEventNames, invalidWidgetTypeNodes, nestedRepeaterNodes, nestedRepeaterMessage, blockSizeNote, heightTypeNote, sizeStringNote } from '../knowledge/normalize.js';
2
+ import { FUNCTIONS_NAMESPACE } from '../knowledge/functionGuards.js';
3
+ import { applyCatalogLayoutRules } from './components.js';
4
+ import { PlatformApiError } from '../platform/errors.js';
5
+ const GUARD_CODES = {
6
+ page: { events: 'PAGE-011', widgets: 'PAGE-012', noun: 'sayfa', tree: 'schema' },
7
+ layout: { events: 'LAY-004', widgets: 'LAY-005', noun: 'layout', tree: 'views' }
8
+ };
9
+ /**
10
+ * JS tanımlayıcısı olmayan ya da AYRILMIŞ `name` alanlarını toplar; ad
11
+ * `const <ad> = Components["<ad>"]` üretimine girer.
12
+ */
13
+ export function invalidComponentNames(nodes) {
14
+ const found = [];
15
+ for (const node of nodes ?? []) {
16
+ const name = node.name;
17
+ if (typeof name === 'string' && name.length > 0 && !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name.replace(/-/g, ''))) {
18
+ found.push(`'${name}' (${node.elementId ?? '?'})`);
19
+ }
20
+ else if (typeof name === 'string' && name.replace(/-/g, '') === FUNCTIONS_NAMESPACE) {
21
+ // K1: preamble her komponent için `const <ad> = Components["<ad>"]` üretir; `Functions` adlı
22
+ // komponent kullanıcı fonksiyonlarının ad alanını ezer ve sayfadaki her Functions.x() ölür.
23
+ found.push(`'${name}' (${node.elementId ?? '?'}) — ayrılmış ad: ${FUNCTIONS_NAMESPACE} kullanıcı fonksiyonlarının ad alanıdır (K1)`);
24
+ }
25
+ found.push(...invalidComponentNames(node.children));
26
+ }
27
+ return found;
28
+ }
29
+ /**
30
+ * Sayfa ve layout yazma yollarının ORTAK kapısı: olay adı (PAGE-011 · LAY-004), widget düğümü
31
+ * (PAGE-012 · LAY-005), komponent adı (PAGE-009) ve iç içe repeater (PAGE-013) denetimleri —
32
+ * hepsi istek gitmeden 400.
33
+ *
34
+ * PAGE-009 ve PAGE-013'ün layout ayağı 2026-09-09'da eklendi: ikisi de yalnız sayfa yollarında
35
+ * koşuyordu, oysa layout `views`'ü aynı runtime'dan geçer — bozuk ad o layout'a bağlı HER
36
+ * sayfanın ifadelerini öldürür, iç içe repeater'ın geri yazma kırılması ise yapısaldır ve
37
+ * barındıranın sayfa mı layout mu olduğuna bakmaz.
38
+ */
39
+ export function assertScreenWriteGuards(kind, screen) {
40
+ const codes = GUARD_CODES[kind];
41
+ const badEvents = invalidScreenEventNames(screen.events, codes.events);
42
+ if (badEvents.length > 0)
43
+ throw new PlatformApiError(`Geçersiz ${codes.noun} olayı (istek gönderilmedi): ${badEvents.join(' · ')}`, 400);
44
+ const badWidgets = invalidWidgetTypeNodes(screen.schema, codes.widgets);
45
+ if (badWidgets.length > 0)
46
+ throw new PlatformApiError(`Geçersiz widget düğümü (istek gönderilmedi): ${badWidgets.join(' · ')}`, 400);
47
+ const badNames = invalidComponentNames(screen.schema);
48
+ if (badNames.length > 0) {
49
+ throw new PlatformApiError(`PAGE-009 — komponent adı JS tanımlayıcısı değil ya da ayrılmış (istek gönderilmedi): ${badNames.join(' · ')}. `
50
+ + 'Runtime her komponent için `const <ad> = Components["<ad>"]` üretir; boşluklu ya da geçersiz bir ad '
51
+ + 'SAYFADAKİ TÜM ifadeleri "Missing initializer in const declaration" ile öldürür (layout adları her sayfaya sızar). '
52
+ + `name alanını elementId ile aynı yaz; okunabilir Türkçe başlık için props.title / props.text / props.label kullan (${codes.noun} ${codes.tree}).`, 400);
53
+ }
54
+ const nested = nestedRepeaterNodes(screen.schema);
55
+ if (nested.length > 0)
56
+ throw new PlatformApiError(nestedRepeaterMessage(nested), 400);
57
+ }
58
+ /**
59
+ * Katalog kurallarını (blok sınırı · auto-height · boyut string'i) ağaca uygular ve
60
+ * `withNormalization` kuyruğuna giren üç notu döndürür. Boş/verilmemiş ağaçta katalog
61
+ * okunmaz — uygulanacak düğüm yoktur, notlar da boş çıkardı.
62
+ */
63
+ export async function screenCatalogNotes(platform, project, environment, applicationId, nodes) {
64
+ if (!Array.isArray(nodes) || nodes.length === 0)
65
+ return [];
66
+ const catalog = await applyCatalogLayoutRules(platform, project, environment, applicationId, nodes);
67
+ return [...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights), ...sizeStringNote(catalog.sizes)];
68
+ }
@@ -31,6 +31,33 @@ export function summarize(node, styleSource) {
31
31
  }
32
32
  return summary;
33
33
  }
34
+ /**
35
+ * Yazma yanıtındaki `appliedPatch` gövdesini iskelete indirger (B-55 · S-9).
36
+ *
37
+ * Alanın KANIT değeri path'indedir: "gerçekten yalnız o komponente mi dokunuldu" sorusunu
38
+ * JSON Pointer cevaplar (`page.md` Kural 2). `value` ise ajanın aynı turda KENDİ gönderdiği
39
+ * gövdenin yankısıdır — `add_component`'te eklenen alt ağacın tamamı, `replace_prop`'ta
40
+ * yazılan Prop'un tamamı (`AppliedJsonPatch(Op, Path, Value)`). 2026-09-06·2 koşusunda on
41
+ * dört yazma çağrısının yanıtı bağlamın en büyük tek kalemiydi.
42
+ *
43
+ * Kanıt kaybı yok: yamanın KAYDEDİLDİĞİNİ `appliedPatch` zaten söylemiyordu (G-23). Onu
44
+ * yamadan sonra okunan şemayla karşılaştıran `schemaOpDrops` söylüyor ve sonucu yanıtta
45
+ * `silentlyDropped` olarak duruyor. Tam gövde `view: 'full'` ile alınır.
46
+ */
47
+ export function outlineAppliedPatch(applied) {
48
+ if (!Array.isArray(applied))
49
+ return applied;
50
+ return applied.map(entry => {
51
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
52
+ return entry;
53
+ const record = entry;
54
+ const value = record.value;
55
+ const node = value && typeof value === 'object' && !Array.isArray(value) && typeof value.elementId === 'string'
56
+ ? summarize(value)
57
+ : undefined;
58
+ return { op: record.op, path: record.path, ...(node ? { component: node } : {}) };
59
+ });
60
+ }
34
61
  /** elementId ya da id ile ağaçta düğüm bulur. */
35
62
  export function findNode(nodes, target) {
36
63
  for (const node of nodes ?? []) {
@@ -77,6 +104,7 @@ export function pageUpdateBody(page, overrides = {}) {
77
104
  name: page.name,
78
105
  slug: page.slug,
79
106
  browserTitle: page.browserTitle ?? '',
107
+ browserTitleKey: page.browserTitleKey ?? '',
80
108
  schema: page.schema ?? [],
81
109
  events: asArray(page.events).filter(event => event?.value),
82
110
  middlewareIds: page.middlewareIds ?? [],
@@ -1,6 +1,30 @@
1
1
  import { guid, pagingParams, readOnly, writes, updates } from './common.js';
2
+ import { withNormalization } from '../knowledge/normalize.js';
2
3
  import { handle } from '../platform/responses.js';
3
4
  import { z } from 'zod';
5
+ /**
6
+ * `environmentId` verilmediğinde platform PRODUCTION'ı paketler
7
+ * (`CreateProjectAsTemplateCommand`: `EnvironmentId ??= ActiveEnvironments().First(f => f.IsProduction)`).
8
+ * Promote edilmemiş projede o environment boştur ve build yine 'success' döner; şablon seed
9
+ * kayıtlarından ibaret kalır. Çağıran bunu yanıttan görmediği için hangi environment'ın
10
+ * paketlendiğini adıyla söylüyoruz (2026-09-12 koşusu S-2).
11
+ */
12
+ async function defaultTemplateEnvironmentNote(platform, projectId) {
13
+ const fallback = "environmentId verilmedi: platform PRODUCTION environment'ını paketler (varsayılan/development değil). Promote edilmemiş projede şablon boş çıkar; development içeriğini istiyorsanız o environment'ın id'sini açıkça verin.";
14
+ try {
15
+ const payload = await platform.org('GET', `/projects/${projectId}`);
16
+ const environments = Array.isArray(payload?.data?.environments)
17
+ ? payload.data.environments
18
+ : [];
19
+ const production = environments.find(environment => environment.isProduction === true);
20
+ if (!production)
21
+ return fallback;
22
+ return `environmentId verilmedi: platform '${String(production.name)}' (production, id ${String(production.id)}) environment'ını paketler — varsayılan/development environment'ı DEĞİL. Oraya henüz promote etmediyseniz şablon yalnız seed kayıtlarını taşır; development içeriğini paketlemek için o environment'ın id'sini açıkça verin.`;
23
+ }
24
+ catch {
25
+ return fallback;
26
+ }
27
+ }
4
28
  export function registerTemplateTools(server, platform) {
5
29
  const config = platform.config;
6
30
  server.registerTool('template_list', {
@@ -24,31 +48,34 @@ export function registerTemplateTools(server, platform) {
24
48
  description: "Şablonun bir modüldeki kayıtlarını listeler (project_create'in templateOptions.filters'ını doldurmak için).",
25
49
  inputSchema: {
26
50
  id: guid,
27
- module: z.string().describe("Module değeri: entity | microservice | workflow | page | layout | component | query | view …")
51
+ module: z.string().describe("Module değeri: entity | microservice | workflow | page | layout | component | query | view | variable | global-function (fonksiyonlar, iki kaynak) | flow … (platform Module enum değeri, kebab-case)")
28
52
  },
29
53
  annotations: readOnly
30
54
  }, handle(config, ({ id, module }) => platform.org('GET', `/templates/${id}/items`, { query: { module } })));
31
55
  server.registerTool('template_create', {
32
56
  title: 'Projeyi şablon olarak yayımla',
33
- description: 'Mevcut projeden yeni bir şablon sürümü üretir. Görsel yükleme bu tool\'da desteklenmez (ikili aktarım kapsam dışı).',
57
+ description: "Mevcut projeden yeni bir şablon sürümü üretir. **Paketlenen environment `environmentId`'dir ve boş bırakılırsa platform PRODUCTION'ı paketler** (`CreateProjectAsTemplateCommand`: `EnvironmentId ??= ActiveEnvironments().First(f => f.IsProduction)`) — 'varsayılan environment' DEĞİL. Henüz promote edilmemiş projede production boştur: build 'success' döner ama şablon yalnız seed kayıtlarını taşır (microfunction/workflow/microservice sıfır). Geliştirmede yazdığınız içeriği paketlemek için development environment id'sini AÇIKÇA verin. Build asenkrondur: yanıt hemen döner, sürüm birkaç saniye sonra template_list'te görünür; içeriği template_items ile doğrulayın. Görsel yükleme bu tool'da desteklenmez (ikili aktarım kapsam dışı).",
34
58
  inputSchema: {
35
59
  projectId: guid,
36
60
  name: z.string().min(1),
37
61
  version: z.string().min(1).describe("Sürüm etiketi, ör. '1.0.0'"),
38
62
  description: z.string().optional(),
39
63
  changeLog: z.string().optional(),
40
- environmentId: guid.optional().describe('Hangi environment\'ın içeriği paketlenecek; boşsa varsayılan')
64
+ environmentId: guid.optional().describe("Hangi environment'ın içeriği paketlenecek. BOŞSA PRODUCTION paketlenir (projenin varsayılan/development environment'ı değil) — development içeriğini şablona koyacaksanız o environment'ın id'sini açıkça verin.")
41
65
  },
42
66
  annotations: writes
43
- }, handle(config, ({ projectId, name, version, description, changeLog, environmentId }) => {
67
+ }, handle(config, async ({ projectId, name, version, description, changeLog, environmentId }) => {
44
68
  const form = new URLSearchParams({ Name: name, Version: version });
69
+ const normalized = [];
45
70
  if (description)
46
71
  form.set('Description', description);
47
72
  if (changeLog)
48
73
  form.set('ChangeLog', changeLog);
49
74
  if (environmentId)
50
75
  form.set('EnvironmentId', environmentId);
51
- return platform.org('POST', `/projects/${projectId}/templates`, { form });
76
+ else
77
+ normalized.push(await defaultTemplateEnvironmentNote(platform, projectId));
78
+ return withNormalization(await platform.org('POST', `/projects/${projectId}/templates`, { form }), normalized);
52
79
  }));
53
80
  server.registerTool('template_check_updates', {
54
81
  title: 'Şablon güncellemesi var mı',
@@ -1,22 +1,38 @@
1
1
  import { guid, projectParam, environmentParam, readOnly, updates } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
3
  import { z } from 'zod';
4
+ import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
5
+ import { readProjectLanguages, translationLanguageFindings } from '../knowledge/languages.js';
6
+ import { policyRules } from '../knowledge/rules.js';
4
7
  const translationKeyInput = z.object({
5
8
  key: z.string().min(1),
6
- values: z.record(z.string()).describe("Dil kodu (ISO 639-1) → çeviri metni; kodlar projenin languages listesinde (project_get/project_update) tanımlı olmalı"),
9
+ values: z.record(z.string().regex(/^[a-z]{2}$/, 'ISO 639-1 iki harfli küçük dil kodu (tr, en, de …); tr-TR gibi bölge ekli kod reddedilir'), z.string()).describe("Dil kodu (ISO 639-1, iki küçük harf) → çeviri metni. Platform kodu yalnız 184 kodluk ISO sözlüğüne göre doğrular (TR-001); PROJENİN languages listesine göre doğrulamaz — o bağı gateway kurar (TR-002)"),
7
10
  componentId: guid.optional().describe("Çeviriyi belirli bir komponente bağlar; o komponent silinince kaydı da silinir")
8
11
  });
12
+ /**
13
+ * Yazma öncesi projenin dil durumunu okur. Proje okunamazsa (uç düşer, yetki yoksa) kontrol
14
+ * ATLANIR ve karar platforma kalır — `project_create`'in slug ön aramasındaki (PRJ-001) davranışın
15
+ * aynısı. Uydurma bulgu üretilmez.
16
+ */
17
+ async function languageFindings(platform, project, keys) {
18
+ const slug = platform.resolveProject(project);
19
+ const codes = keys.flatMap(entry => Object.keys(entry.values ?? {}));
20
+ if (codes.length === 0)
21
+ return [];
22
+ const payload = await platform.org('GET', `/p/${slug}`).catch(() => undefined);
23
+ return translationLanguageFindings(readProjectLanguages(payload), codes, slug);
24
+ }
9
25
  export function registerTranslationTools(server, platform) {
10
26
  const config = platform.config;
11
27
  server.registerTool('translation_list', {
12
28
  title: 'Çevirileri listele',
13
- description: "Application'ın tüm çeviri kayıtlarını (dil × anahtar × değer) düz liste olarak döner. Proje dilleri/varsayılan dil için project_get; sayfada bir prop'u çeviriye bağlamak için platform_guide('translation').",
29
+ description: "Application'ın tüm çeviri kayıtlarını (dil × anahtar × değer) düz liste olarak döner. Proje dilleri/varsayılan dil için project_get (yanıtındaki localization bloğu runtime'ın bu çevirileri çözüp çözemeyeceğini söyler); sayfada bir prop'u çeviriye bağlamak için platform_guide('translation').",
14
30
  inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
15
31
  annotations: readOnly
16
32
  }, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, `/translations/${applicationId}`, { environment })));
17
33
  server.registerTool('translation_update', {
18
34
  title: 'Çevirileri güncelle',
19
- description: "Bir ya da daha çok çeviri anahtarını toplu ekler/günceller (keys) ve/veya siler (deletedKeys) — tek çağrıda ikisi de verilebilir. 'keys' üzerinden yeni bir anahtar da açılabilir (upsert). 'values'taki dil kodları projenin languages listesinde tanımlı olmalı, aksi hâlde platform reddeder.",
35
+ description: "Bir ya da daha çok çeviri anahtarını toplu ekler/günceller (keys) ve/veya siler (deletedKeys) — tek çağrıda ikisi de verilebilir. 'keys' üzerinden yeni bir anahtar da açılabilir (upsert; verilmeyen diller silinmez, korunur). DİKKAT: çeviri yazmak tek başına YETMEZ — runtime bir etiketi ancak projenin languages listesi ve defaultLanguage kuruluysa çözer, aksi hâlde ekrana ham anahtar basar ve yazma yine 200 döner (ölçüldü 2026-09-09, S-15). Gateway yazmadan önce projeyi okur ve bağ kopuksa TR-002 caution'ı düşer; dilleri project_update(languages, defaultLanguage) ile kurun. Backend ön kontrolü (TR-001, block): anahtarlar boş olamaz ve tekil, dil kodları ISO 639-1 listesinden, deletedKeys boş olamaz.",
20
36
  inputSchema: {
21
37
  project: projectParam,
22
38
  environment: environmentParam,
@@ -25,13 +41,13 @@ export function registerTranslationTools(server, platform) {
25
41
  deletedKeys: z.array(z.string().min(1)).optional().describe('Silinecek anahtar adları')
26
42
  },
27
43
  annotations: updates
28
- }, handle(config, ({ project, environment, applicationId, keys, deletedKeys }) => {
44
+ }, handle(config, async ({ project, environment, applicationId, keys, deletedKeys }) => {
29
45
  if (!keys?.length && !deletedKeys?.length) {
30
46
  throw new Error("'keys' ya da 'deletedKeys' parametrelerinden en az biri dolu olmalı.");
31
47
  }
32
- return platform.tenant('PUT', project, `/translations/${applicationId}`, {
33
- environment,
34
- body: { keys: keys ?? [], deletedKeys: deletedKeys ?? [] }
35
- });
48
+ const body = { keys: keys ?? [], deletedKeys: deletedKeys ?? [] };
49
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'translation', body, { operation: 'update' }));
50
+ const languages = await languageFindings(platform, project, keys ?? []);
51
+ return attachFindings(await platform.tenant('PUT', project, `/translations/${applicationId}`, { environment, body }), [...findings, ...languages]);
36
52
  }));
37
53
  }
@@ -3,6 +3,12 @@ import { guid, projectParam, environmentParam, definitionParam, pagingParams, re
3
3
  import { PlatformApiError } from '../platform/errors.js';
4
4
  import { handle } from '../platform/responses.js';
5
5
  import { z } from 'zod';
6
+ import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
7
+ import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
8
+ import { policyRules } from '../knowledge/rules.js';
9
+ import { constraintSummary } from '../knowledge/backendConstraints.js';
10
+ const policyOverrideParam = z.boolean().optional()
11
+ .describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
6
12
  export function registerViewTools(server, platform) {
7
13
  const config = platform.config;
8
14
  const normalizeSource = (definition) => {
@@ -19,29 +25,33 @@ export function registerViewTools(server, platform) {
19
25
  }, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/views', { environment, query })));
20
26
  server.registerTool('view_get', {
21
27
  title: 'View getir',
22
- description: 'Tek view tanımını getirir; view_create için en iyi gövde şablonudur.',
28
+ description: "Tek view tanımını getirir; view_create için en iyi gövde şablonudur. Hatırlatma: view TAM METİN ARAMA indeksidir, agregasyon/raporlama aracı değildir — özet için query_create (09-02 M-3).",
23
29
  inputSchema: { project: projectParam, environment: environmentParam, id: guid },
24
30
  annotations: readOnly
25
31
  }, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/views/${id}`, { environment })));
26
32
  server.registerTool('view_create', {
27
33
  title: 'View oluştur',
28
- description: "TAM METİN ARAMA indeksi tanımlar (definition = CreateViewCommand; gövde şeması ngapps://schemas/CreateViewCommand, analizör sözlüğü platform_guide('view')) — raporlama/agregasyon aracı DEĞİLDİR. Seçilen tablo/alanlar analizörden (tokenizer/dil) geçirilip slug adlı projeksiyon tablosuna indekslenir; tüketimi source:'view' microservice'idir ve o uç ZORUNLU 'searchText' parametresi ister (+limit/offset). Rapor/özet için raw query (query_create) + query kaynaklı microservice kullanın. Birden çok entity verilebilir (entities: [{entityId, alias}], fields alias'la bağlanır: viewEntityAlias) ama bu JOIN DEĞİLDİR: her tablo ayrı ayrı indekslenir, aralarında ilişki tanımı yoktur ve eşleşme hangi tablodan geldiyse dönen kayıt o tablonun satırıdır. İlişkili içeriği tek kayıtta aratmak için denormalize bir alan kullanın (yorum metnini talebe yazan automation + tek entity'li view).",
29
- inputSchema: { project: projectParam, environment: environmentParam, definition: definitionParam },
34
+ description: "TAM METİN ARAMA indeksi tanımlar (definition = CreateViewCommand; gövde şeması ngapps://schemas/CreateViewCommand, analizör sözlüğü platform_guide('view')) — raporlama/agregasyon aracı DEĞİLDİR. Seçilen tablo/alanlar analizörden (tokenizer/dil) geçirilip slug adlı projeksiyon tablosuna indekslenir; tüketimi source:'view' microservice'idir ve o uç ZORUNLU 'searchText' parametresi ister (+limit/offset). Rapor/özet için raw query (query_create) + query kaynaklı microservice kullanın. Birden çok entity verilebilir (entities: [{entityId, alias}], fields alias'la bağlanır: viewEntityAlias) ama bu JOIN DEĞİLDİR: her tablo ayrı ayrı indekslenir, aralarında ilişki tanımı yoktur ve eşleşme hangi tablodan geldiyse dönen kayıt o tablonun satırıdır. İlişkili içeriği tek kayıtta aratmak için denormalize bir alan kullanın (yorum metnini talebe yazan automation + tek entity'li view). " + constraintSummary('CreateViewCommand'),
35
+ inputSchema: { project: projectParam, environment: environmentParam, definition: definitionParam, policyOverride: policyOverrideParam },
30
36
  annotations: writes
31
- }, handle(config, async ({ project, environment, definition }) => {
37
+ }, handle(config, async ({ project, environment, definition, policyOverride }) => {
32
38
  const fixes = normalizeSource(definition);
39
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'view', definition, { operation: 'create' }, policyOverride));
40
+ const drift = unmappedMembers('CreateViewCommand', definition);
33
41
  const response = await platform.tenant('POST', project, '/views', { environment, body: definition });
34
- return withNormalization(response, viewAliasNote(fixes));
42
+ return attachFindings(withUnmappedMembers(withNormalization(response, viewAliasNote(fixes)), drift), findings);
35
43
  }));
36
44
  server.registerTool('view_update', {
37
45
  title: 'View güncelle',
38
- description: "View tanımını günceller (definition = UpdateViewCommand, şema ngapps://schemas/UpdateViewCommand); mevcut hâli view_get ile alın. UpdateViewCommand'da datasourceId YOKTUR (create'te bağlanır). DİKKAT: view_get alanları 'viewEntityId' ile döndürür, yazma tarafı 'viewEntityAlias' ister — okuduğunuzu birebir geri yazarsanız gateway id'yi alias'a çevirir, çeviremezse isteği reddeder.",
39
- inputSchema: { project: projectParam, environment: environmentParam, id: guid, definition: definitionParam },
46
+ description: "View tanımını günceller (definition = UpdateViewCommand, şema ngapps://schemas/UpdateViewCommand); mevcut hâli view_get ile alın. UpdateViewCommand'da datasourceId YOKTUR (create'te bağlanır). DİKKAT: view_get alanları 'viewEntityId' ile döndürür, yazma tarafı 'viewEntityAlias' ister — okuduğunuzu birebir geri yazarsanız gateway id'yi alias'a çevirir, çeviremezse isteği reddeder. " + constraintSummary('UpdateViewCommand'),
47
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
40
48
  annotations: updates
41
- }, handle(config, async ({ project, environment, id, definition }) => {
49
+ }, handle(config, async ({ project, environment, id, definition, policyOverride }) => {
42
50
  const fixes = normalizeSource(definition);
51
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'view', definition, { operation: 'update' }, policyOverride));
52
+ const drift = unmappedMembers('UpdateViewCommand', definition);
43
53
  const response = await platform.tenant('PUT', project, `/views/${id}`, { environment, body: definition });
44
- return withNormalization(response, viewAliasNote(fixes));
54
+ return attachFindings(withUnmappedMembers(withNormalization(response, viewAliasNote(fixes)), drift), findings);
45
55
  }));
46
56
  server.registerTool('view_delete', {
47
57
  title: 'View sil',
@@ -51,7 +61,7 @@ export function registerViewTools(server, platform) {
51
61
  }, handle(config, ({ project, environment, id }) => platform.tenant('DELETE', project, `/views/${id}`, { environment })));
52
62
  server.registerTool('view_rebuild', {
53
63
  title: 'View yeniden kur',
54
- description: 'View projeksiyon tablosunu yeniden inşa eder (veri değişikliklerinden sonra senkron için).',
64
+ description: "View projeksiyon tablosunu yeniden inşa eder (veri değişikliklerinden sonra senkron için). Rebuild ARAMA indeksini tazeler; sayım/özet üretmez — raporlama için query_create (09-02 M-3).",
55
65
  inputSchema: { project: projectParam, environment: environmentParam, id: guid },
56
66
  annotations: writes
57
67
  }, handle(config, ({ project, environment, id }) => platform.tenant('POST', project, `/views/${id}/rebuild`, { environment })));
@@ -1,11 +1,16 @@
1
- import { guid, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
1
+ import { guid, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive, microOps, languageParam } from './common.js';
2
2
  import { normalizeWorkflowDefinition, withNormalization } from '../knowledge/normalize.js';
3
3
  import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
4
4
  import { workflowOutline, resolvePart, resolvePatchOperations } from '../knowledge/outlines.js';
5
- import { policyRules } from '../knowledge/rules.js';
5
+ import { patchResult, baseVersionOf } from '../platform/patch.js';
6
+ import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
7
+ import { policyRules, microfunctionStepFindings } from '../knowledge/rules.js';
8
+ import { languageHeader } from '../knowledge/languageCodes.js';
6
9
  import { handle } from '../platform/responses.js';
7
10
  import fastJsonPatch from 'fast-json-patch';
8
11
  import { z } from 'zod';
12
+ import { constraintSummary } from '../knowledge/backendConstraints.js';
13
+ import { unmappedMembers, introducedUnmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
9
14
  const policyOverrideParam = z.boolean().optional()
10
15
  .describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
11
16
  const patchOperationsParam = z.array(z.record(z.unknown())).min(1)
@@ -106,44 +111,65 @@ export function registerWorkflowTools(server, platform) {
106
111
  }));
107
112
  server.registerTool('workflow_create', {
108
113
  title: 'Workflow oluştur',
109
- description: "Yeni workflow tanımlar (definition = CreateWorkflowCommand). type: 'workflow' | 'automation' | 'middleware'. İlk kullanımdan önce platform_guide('workflow') okuyun: DTO-önce akışı, branch bağlama (branches[].nextStepId), tip×step matrisi. Her adım ve akışın kendisi 'description' taşımak ZORUNDADIR (WF-024, block): adımın NE İŞE YARADIĞINI 1-2 cümleyle yazın — Studio kanvasında ⓘ ikonu olarak görünür; mekanizmayı değil niyeti yazın. Middleware (istek öncesi koşan guard/setter, user değişkeni, alfabetik sıra) için platform_guide('middleware') + ngapps://examples/middleware-authenticate. Adımın gerçek bir tuzağı varsa (idempotent değil, kapsam adımda değil, dış bağımlılık, sessiz düşen dal) 'caution' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın; doldurma metin yazmayın. Gövde politika kontrolünden geçer.",
114
+ description: "Yeni workflow tanımlar (definition = CreateWorkflowCommand). type: 'workflow' | 'automation' | 'middleware'. İlk kullanımdan önce platform_guide('workflow') okuyun: DTO-önce akışı, branch bağlama (branches[].nextStepId), tip×step matrisi. Her adım ve akışın kendisi 'description' taşımak ZORUNDADIR (WF-024, block): adımın NE İŞE YARADIĞINI 1-2 cümleyle yazın — Studio kanvasında ⓘ ikonu olarak görünür; mekanizmayı değil niyeti yazın. Middleware (istek öncesi koşan guard/setter, user değişkeni, alfabetik sıra) için platform_guide('middleware') + ngapps://examples/middleware-authenticate. Adımın gerçek bir tuzağı varsa (idempotent değil, kapsam adımda değil, dış bağımlılık, sessiz düşen dal) 'caution' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın; doldurma metin yazmayın. Gövde politika kontrolünden geçer. " + constraintSummary('CreateWorkflowCommand'),
110
115
  inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
111
116
  annotations: writes
112
117
  }, handle(config, async ({ project, environment, applicationId, definition, policyOverride }) => {
113
118
  const normalized = normalizeWorkflowDefinition(definition);
114
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'create', workflowKind: definition.type }, policyOverride));
119
+ const findings = enforcePolicy([
120
+ ...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'create', workflowKind: definition.type }, policyOverride),
121
+ ...await microfunctionStepFindings(platform, project, environment, definition, definition.type, policyOverride)
122
+ ]);
123
+ const drift = unmappedMembers('CreateWorkflowCommand', definition);
115
124
  const payload = await platform.tenant('POST', project, `/workflows/${applicationId}`, { environment, body: definition });
116
- return attachFindings(withNormalization(payload, normalized), findings);
125
+ return attachFindings(withUnmappedMembers(withWriteDiff(withNormalization(payload, normalized), writeDiff(definition, payload)), drift), findings);
117
126
  }));
118
127
  server.registerTool('workflow_update', {
119
128
  title: 'Workflow güncelle',
120
- description: 'Workflow tanımını günceller (definition = UpdateWorkflowCommand, tam gövde). Hedefli değişiklik için workflow_patch tercih edin. Her adım ve akışın kendisi \'description\' taşımak ZORUNDADIR (WF-024, block). Adımın gerçek bir tuzağı varsa (idempotent değil, kapsam adımda değil, dış bağımlılık, sessiz düşen dal) \'caution\' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın; doldurma metin yazmayın. Gövde politika kontrolünden geçer.',
129
+ description: 'Workflow tanımını günceller (definition = UpdateWorkflowCommand, tam gövde). Hedefli değişiklik için workflow_patch tercih edin. Her adım ve akışın kendisi \'description\' taşımak ZORUNDADIR (WF-024, block). Adımın gerçek bir tuzağı varsa (idempotent değil, kapsam adımda değil, dış bağımlılık, sessiz düşen dal) \'caution\' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın; doldurma metin yazmayın. Gövde politika kontrolünden geçer. ' + constraintSummary('UpdateWorkflowCommand'),
121
130
  inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
122
131
  annotations: updates
123
132
  }, handle(config, async ({ project, environment, applicationId, id, definition, policyOverride }) => {
124
133
  const normalized = normalizeWorkflowDefinition(definition);
125
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'update', workflowKind: definition.type }, policyOverride));
134
+ const findings = enforcePolicy([
135
+ ...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'update', workflowKind: definition.type }, policyOverride),
136
+ ...await microfunctionStepFindings(platform, project, environment, definition, definition.type, policyOverride)
137
+ ]);
138
+ const drift = unmappedMembers('UpdateWorkflowCommand', definition);
126
139
  const payload = await platform.tenant('PUT', project, `/workflows/${applicationId}/${id}`, { environment, body: definition });
127
- return attachFindings(withNormalization(payload, normalized), findings);
140
+ return attachFindings(withUnmappedMembers(withWriteDiff(withNormalization(payload, normalized), writeDiff(definition, payload)), drift), findings);
128
141
  }));
129
142
  server.registerTool('workflow_patch', {
130
143
  title: 'Workflow\'a patch uygula',
131
- description: 'Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut workflow\'u çeker, JSON Patch işlemlerini uygular, politika kontrolünden geçirir, tam gövdeyi geri yazar. Değişiklik hedefli ama yazma değil: hata yolu olmayan bir workflow/middleware kaydına patch atmak o kaydın grafiğini de düzeltir — start adımına global-error dalı + 500 dönen response adımı eklenir ve yanıtta gatewayNormalized ile bildirilir. Yanıt outline özetidir.',
132
- inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, operations: patchOperationsParam, policyOverride: policyOverrideParam },
144
+ description: 'Tam gövdeyi bağlama almadan hedefli değişiklik: gateway mevcut workflow\'u çeker, JSON Patch işlemlerini uygular, politika kontrolünden geçirir ve FARKI platformun PATCH /workflows/{app}/{id} ucuna `{ baseVersion, ops }` olarak yazar — tam gövde PUT\'u ATILMAZ. `baseVersion` okunan kaydın `version` alanıdır; kayıt bu arada değiştiyse platform 409 döner ve hiçbir op uygulanmaz. Değişiklik hedefli ama yazma değil: hata yolu olmayan bir workflow/middleware kaydına patch atmak o kaydın grafiğini de düzeltir — start adımına global-error dalı + 500 dönen response adımı eklenir ve yanıtta gatewayNormalized ile bildirilir. Yanıt outline özetidir.',
145
+ inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, operations: patchOperationsParam.optional(), ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı) — sayfa micro-op tool'larıyla aynı adı kullanabilmeniz için"), policyOverride: policyOverrideParam },
133
146
  annotations: updates
134
- }, handle(config, async ({ project, environment, applicationId, id, operations, policyOverride }) => {
147
+ }, handle(config, async ({ project, environment, applicationId, id, operations: givenOperations, ops, policyOverride }) => {
148
+ const operations = microOps('workflow_patch', ops, givenOperations);
135
149
  const current = await platform.tenant('GET', project, `/workflows/${applicationId}/${id}`, { environment });
136
150
  const definition = structuredClone((current?.data ?? current));
137
151
  const resolved = resolvePatchOperations(definition, operations);
138
152
  const patched = fastJsonPatch.applyPatch(definition, resolved.operations, true, false).newDocument;
139
153
  const normalized = normalizeWorkflowDefinition(patched);
140
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', patched, { operation: 'update', workflowKind: patched.type }, policyOverride));
141
- await platform.tenant('PUT', project, `/workflows/${applicationId}/${id}`, { environment, body: patched });
142
- return attachFindings(withNormalization({
154
+ const findings = enforcePolicy([
155
+ ...evaluatePolicy(config, policyRules, 'workflow', patched, { operation: 'update', workflowKind: patched.type }, policyOverride),
156
+ ...await microfunctionStepFindings(platform, project, environment, patched, patched.type, policyOverride)
157
+ ]);
158
+ const drift = introducedUnmappedMembers('UpdateWorkflowCommand', (current?.data ?? current), patched);
159
+ // Gövde değil FARK gönderilir: gateway normalizasyonları da op'a çevrilsin diye net diff
160
+ // okunan kayıt ile son hâl arasından hesaplanır (tam PUT yok, yalnız değişen alanlar yazılır).
161
+ const netOps = fastJsonPatch.compare(definition, patched);
162
+ const saved = await platform.tenant('PATCH', project, `/workflows/${applicationId}/${id}`, {
163
+ environment,
164
+ body: { baseVersion: baseVersionOf(definition, 'workflow_get'), ops: netOps }
165
+ });
166
+ const written = patchResult(saved, patched);
167
+ return attachFindings(withUnmappedMembers(withNormalization({
143
168
  applied: operations.length,
169
+ newVersion: written.newVersion,
144
170
  patchTargets: resolved.targets,
145
- outline: workflowOutline(patched)
146
- }, [...normalized, ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)]), findings);
171
+ outline: workflowOutline(written.data)
172
+ }, [...normalized, ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)]), drift), findings);
147
173
  }));
148
174
  server.registerTool('workflow_delete', {
149
175
  title: 'Workflow sil',
@@ -159,13 +185,22 @@ export function registerWorkflowTools(server, platform) {
159
185
  }, handle(config, ({ project, environment, id }) => platform.tenant('POST', project, `/workflows/clone/${id}`, { environment })));
160
186
  server.registerTool('workflow_preview', {
161
187
  title: 'Workflow önizle',
162
- description: "Kaydedilmemiş workflow tanımını çalıştırır; kaydetmeden önce her akışı preview ile doğrulayın. definition = PreviewWorkflowCommand gövdesi. Test girdisi 'previewPayload' anahtarıyla verilir ('parameters'/'input' ÇALIŞMAZ), değişkenler 'previewVariables'. previewPayload STRING olmalıdır (JSON.stringify(...) ya da \"({ id: 1 })\") — nesne gönderilirse platform 'Unexpected end of input' ile 500 döner. Preview kuru çalıştırma değildir: table create/update step'leri kalıcı satır yazar.",
163
- inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
188
+ description: "Kaydedilmemiş workflow tanımını çalıştırır; kaydetmeden önce her akışı preview ile doğrulayın. definition = PreviewWorkflowCommand gövdesi. Test girdisi 'previewPayload' anahtarıyla verilir ('parameters'/'input' ÇALIŞMAZ), değişkenler 'previewVariables'. previewPayload STRING olmalıdır (JSON.stringify(...) ya da \"({ id: 1 })\") — nesne gönderilirse platform 'Unexpected end of input' ile 500 döner. Akışın parameterDtoId'si YOKSA previewPayload verilmez (platform 400 döner; gateway alanı düşürüp gatewayNormalized ile bildirir). Preview tek transaction'da koşar ve sonunda ROLLBACK edilir: table create/update step'leri kalıcı satır YAZMAZ (ölçüldü, İK koşusu W-1). Tek istisna code step içindeki dış HTTP çağrılarıdır — onlar geri alınamaz. Davranış kuralları: platform_guide('workflow'). " + constraintSummary('PreviewWorkflowCommand'),
189
+ inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, definition: definitionParam, policyOverride: policyOverrideParam, language: languageParam },
164
190
  annotations: writes
165
- }, handle(config, async ({ project, environment, applicationId, definition, policyOverride }) => {
191
+ }, handle(config, async ({ project, environment, applicationId, definition, policyOverride, language }) => {
166
192
  const normalized = normalizeWorkflowDefinition(definition);
167
- const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'preview', workflowKind: definition.type }, policyOverride));
168
- const payload = await platform.tenant('POST', project, `/workflows/${applicationId}/preview`, { environment, body: definition });
193
+ // 33/S-6: parameterDtoId olmayan akışa previewPayload gönderilince platform 400 verir
194
+ // ("'previewPayload' requires a parameter DTO"). Girdisiz akışta alanın anlamı yok — düşürülür.
195
+ if (!definition.parameterDtoId && definition.previewPayload !== undefined) {
196
+ delete definition.previewPayload;
197
+ normalized.push("previewPayload düşürüldü — akışın parameterDtoId'si yok, platform 400 \"'previewPayload' requires a parameter DTO\" verir; girdi gerekiyorsa önce parameterDtoId + parameterBehavior tanımlayın");
198
+ }
199
+ const findings = enforcePolicy([
200
+ ...evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'preview', workflowKind: definition.type }, policyOverride),
201
+ ...await microfunctionStepFindings(platform, project, environment, definition, definition.type, policyOverride)
202
+ ]);
203
+ const payload = await platform.tenant('POST', project, `/workflows/${applicationId}/preview`, { environment, body: definition, headers: languageHeader(language, normalized) });
169
204
  return attachFindings(withNormalization(payload, normalized), findings);
170
205
  }));
171
206
  server.registerTool('workflow_step_schema', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "description": "Zeyrek (NgApps) platform APIs as MCP tools — data, logic and Studio design surface in one gateway",
6
6
  "license": "UNLICENSED",
@@ -29,8 +29,13 @@
29
29
  "test": "vitest run",
30
30
  "docs:lint": "tsx scripts/docs-lint.ts",
31
31
  "generate-schemas": "tsx scripts/generate-schemas.ts",
32
+ "refresh-schemas": "scripts/refresh-schemas.sh",
33
+ "schema:check": "tsx scripts/schemaFingerprint.ts",
32
34
  "generate-reserved-tables": "tsx scripts/generate-reserved-tables.ts",
33
- "harvest-examples": "tsx scripts/harvest-examples.ts"
35
+ "harvest-examples": "tsx scripts/harvest-examples.ts",
36
+ "kabul:import-adres": "tsx scripts/import-kabul-adres.ts",
37
+ "kabul:import-senaryolar": "tsx scripts/import-kabul-senaryolar.ts",
38
+ "benchmark:report": "tsx scripts/benchmark-report.ts"
34
39
  },
35
40
  "dependencies": {
36
41
  "@modelcontextprotocol/sdk": "^1.12.0",