@appaflytech/zeyrek-ai-mcp 0.0.43 → 0.0.45

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 (65) hide show
  1. package/dist/knowledge/backendConstraints.js +86 -2
  2. package/dist/knowledge/componentCatalog.js +63 -26
  3. package/dist/knowledge/componentEvents.js +6 -0
  4. package/dist/knowledge/connectDataBind.js +43 -6
  5. package/dist/knowledge/connectDataDefinition.js +74 -3
  6. package/dist/knowledge/connectDataRules.js +14 -0
  7. package/dist/knowledge/mobileComponentCatalog.js +66 -7
  8. package/dist/knowledge/mobileStore.js +61 -2
  9. package/dist/knowledge/platform/mobile.js +1 -1
  10. package/dist/knowledge/platform/mobileRules.js +2 -1
  11. package/dist/knowledge/platform/mobileTabs.js +31 -2
  12. package/dist/knowledge/platform/web.js +1 -1
  13. package/dist/knowledge/rules.js +195 -4
  14. package/dist/knowledge/textHooks.js +434 -0
  15. package/dist/platform/capture/capture.js +1 -1
  16. package/dist/platform/capture/readiness.js +4 -0
  17. package/dist/platform/errors.js +22 -2
  18. package/dist/prompts/mobilePublish.js +21 -0
  19. package/dist/resources/examples/flow-biometric-confirm.json +199 -0
  20. package/dist/resources/examples/microfunction-node-dependencies.json +19 -0
  21. package/dist/resources/guides/access.md +3 -3
  22. package/dist/resources/guides/component.md +20 -4
  23. package/dist/resources/guides/deployment.md +98 -0
  24. package/dist/resources/guides/entity.md +1 -1
  25. package/dist/resources/guides/expressions.md +5 -1
  26. package/dist/resources/guides/external-component.md +6 -4
  27. package/dist/resources/guides/merge.md +81 -4
  28. package/dist/resources/guides/microfunction.md +104 -9
  29. package/dist/resources/guides/microservice.md +34 -5
  30. package/dist/resources/guides/mobile-connect-data.md +70 -0
  31. package/dist/resources/guides/mobile-flow.md +64 -0
  32. package/dist/resources/guides/mobile-publish.md +161 -16
  33. package/dist/resources/guides/mobile-steps.md +17 -0
  34. package/dist/resources/guides/mobile.md +41 -23
  35. package/dist/resources/guides/page.md +11 -5
  36. package/dist/resources/guides/translation.md +95 -0
  37. package/dist/resources/knowledge.js +3 -1
  38. package/dist/resources/prompts/mobile-publish-prepare.md +89 -0
  39. package/dist/resources/schema-fingerprint.json +4 -4
  40. package/dist/resources/schemas/ApplyMicrofunctionArchiveChangesCommand.json +76 -0
  41. package/dist/resources/schemas/ConnectDataDescribeDto.json +44 -0
  42. package/dist/resources/schemas/ConnectDataResultDto.json +10 -0
  43. package/dist/resources/schemas/CreateMicrofunctionCommand.json +8 -0
  44. package/dist/resources/schemas/InvokeMicrofunctionCommand.json +0 -4
  45. package/dist/resources/schemas/MobileStorePreflightScope.json +13 -0
  46. package/dist/resources/schemas/StartMobileBuildCommand.json +35 -1
  47. package/dist/resources/schemas/UpdateMicrofunctionCommand.json +8 -0
  48. package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +75 -0
  49. package/dist/server.js +2 -0
  50. package/dist/tools/common.js +6 -0
  51. package/dist/tools/components.js +19 -6
  52. package/dist/tools/connectData.js +9 -4
  53. package/dist/tools/deployments.js +1 -1
  54. package/dist/tools/designSchemaPatch.js +16 -4
  55. package/dist/tools/environments.js +3 -3
  56. package/dist/tools/files.js +1 -1
  57. package/dist/tools/flows.js +29 -3
  58. package/dist/tools/layouts.js +32 -12
  59. package/dist/tools/merges.js +8 -8
  60. package/dist/tools/microfunctions.js +53 -3
  61. package/dist/tools/mobileStore.js +83 -21
  62. package/dist/tools/pages.js +59 -24
  63. package/dist/tools/platformInfo.js +4 -2
  64. package/dist/tools/textHookGuard.js +116 -0
  65. package/package.json +2 -2
@@ -11,6 +11,8 @@ import { PlatformApiError } from '../platform/errors.js';
11
11
  import { handle } from '../platform/responses.js';
12
12
  import { applicationTypeField, resolveProfile } from '../knowledge/platform/resolve.js';
13
13
  import { screenProfileWarnings } from './profileWrite.js';
14
+ import { enforceTextHooks, textHookOverrideParam } from './textHookGuard.js';
15
+ import { attachFindings } from '../knowledge/policy.js';
14
16
  import { jsonArray } from '../platform/jsonArray.js';
15
17
  import { z } from 'zod';
16
18
  const patchOperationsParam = jsonArray(z.array(z.record(z.unknown())).min(1))
@@ -111,14 +113,16 @@ export function registerLayoutTools(server, platform) {
111
113
  name: z.string().min(1),
112
114
  views: viewsParam.optional(),
113
115
  events: eventsParam.optional(),
114
- parameters: z.record(z.unknown()).optional()
116
+ parameters: z.record(z.unknown()).optional(),
117
+ policyOverride: textHookOverrideParam
115
118
  },
116
119
  annotations: writes
117
- }, handle(config, async ({ project, environment, applicationId, name, views, events, parameters }) => {
120
+ }, handle(config, async ({ project, environment, applicationId, name, views, events, parameters, policyOverride }) => {
118
121
  const resolved = await resolveProfile(platform, applicationId);
119
122
  const named = fillComponentNames(views);
120
123
  assertScreenWriteGuards('layout', { schema: views, events }, resolved.profile.platform);
121
124
  assertNoRetiredNodes(views);
125
+ const textFindings = await enforceTextHooks(config, { platform, project, environment, applicationId, after: views ?? [], where: `layout '${name}'` }, policyOverride);
122
126
  const fixed = normalizeComponentDefaults(views);
123
127
  const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, views, 'layout_create');
124
128
  const gaps = [
@@ -135,10 +139,10 @@ export function registerLayoutTools(server, platform) {
135
139
  const normalized = withWriteDiff(withNormalization(response, [...componentNameNote(named), ...componentDefaultsNote(fixed), ...catalogNotes, ...slotWarning(views)]), drops);
136
140
  const typeField = applicationTypeField(resolved);
137
141
  if (!normalized || typeof normalized !== 'object')
138
- return normalized;
142
+ return attachFindings(normalized, textFindings);
139
143
  if (gaps.length === 0)
140
- return Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField };
141
- return { ...normalized, ...typeField, layoutWarnings: gaps };
144
+ return attachFindings(Object.keys(typeField).length === 0 ? normalized : { ...normalized, ...typeField }, textFindings);
145
+ return attachFindings({ ...normalized, ...typeField, layoutWarnings: gaps }, textFindings);
142
146
  }));
143
147
  server.registerTool('layout_update', {
144
148
  title: 'Layout güncelle',
@@ -151,15 +155,24 @@ export function registerLayoutTools(server, platform) {
151
155
  name: z.string().min(1).optional(),
152
156
  views: viewsParam.optional(),
153
157
  events: eventsParam.optional(),
154
- parameters: z.record(z.unknown()).optional()
158
+ parameters: z.record(z.unknown()).optional(),
159
+ policyOverride: textHookOverrideParam
155
160
  },
156
161
  annotations: updates
157
- }, handle(config, async ({ project, environment, applicationId, id, ...changes }) => {
162
+ }, handle(config, async ({ project, environment, applicationId, id, policyOverride, ...changes }) => {
158
163
  const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
159
164
  const resolved = overrides.views === undefined ? undefined : await resolveProfile(platform, applicationId);
160
165
  const named = fillComponentNames(overrides.views);
161
166
  assertScreenWriteGuards('layout', { schema: overrides.views, events: overrides.events }, resolved?.profile.platform);
162
167
  const current = await fetchHost(platform, 'layout', project, environment, applicationId, id);
168
+ const textFindings = overrides.views === undefined
169
+ ? []
170
+ : await enforceTextHooks(config, {
171
+ platform, project, environment, applicationId,
172
+ after: overrides.views,
173
+ before: async () => current.views,
174
+ where: `layout '${String(current.name ?? id)}'`
175
+ }, policyOverride);
163
176
  const fixed = normalizeComponentDefaults(overrides.views);
164
177
  const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, overrides.views, 'layout_update');
165
178
  const body = layoutUpdateBody({ ...current, applicationId }, overrides);
@@ -175,7 +188,7 @@ export function registerLayoutTools(server, platform) {
175
188
  const summary = { id, changed: Object.keys(overrides), preserved: { viewNodes: body.views.length } };
176
189
  if (gaps.length > 0)
177
190
  summary.layoutWarnings = gaps;
178
- return withNormalization(summary, [...componentNameNote(named), ...componentDefaultsNote(fixed), ...catalogNotes, ...slotWarning(body.views)]);
191
+ return attachFindings(withNormalization(summary, [...componentNameNote(named), ...componentDefaultsNote(fixed), ...catalogNotes, ...slotWarning(body.views)]), textFindings);
179
192
  }));
180
193
  server.registerTool('layout_patch', {
181
194
  title: "Layout'a patch uygula",
@@ -187,10 +200,11 @@ export function registerLayoutTools(server, platform) {
187
200
  id: guid,
188
201
  operations: patchOperationsParam.optional(),
189
202
  ops: patchOperationsParam.optional().describe("'operations' ile aynı alan (eşanlamlı); JSON Patch işlemleriyle set_prop_type micro-op'ları KARIŞIK verilebilir"),
190
- expectedUpdatedAt: z.string().optional().describe("layout_schema_get çıktısındaki updatedAt; eşleşmezse patch uygulanmaz (araya giren yazmayı ezmeyi engeller)")
203
+ expectedUpdatedAt: z.string().optional().describe("layout_schema_get çıktısındaki updatedAt; eşleşmezse patch uygulanmaz (araya giren yazmayı ezmeyi engeller)"),
204
+ policyOverride: textHookOverrideParam
191
205
  },
192
206
  annotations: updates
193
- }, handle(config, async ({ project, environment, applicationId, id, operations: givenOperations, ops, expectedUpdatedAt }) => {
207
+ }, handle(config, async ({ project, environment, applicationId, id, operations: givenOperations, ops, expectedUpdatedAt, policyOverride }) => {
194
208
  const operations = microOps('layout_patch', ops, givenOperations);
195
209
  const current = await fetchHost(platform, 'layout', project, environment, applicationId, id);
196
210
  const audit = current.audit;
@@ -235,6 +249,12 @@ export function registerLayoutTools(server, platform) {
235
249
  const fixed = normalizeComponentDefaults(overrides.views);
236
250
  const catalogNotes = await assertScreenCatalog(platform, project, environment, applicationId, overrides.views, 'layout_patch');
237
251
  const body = layoutUpdateBody({ ...current, applicationId }, overrides);
252
+ const textFindings = await enforceTextHooks(config, {
253
+ platform, project, environment, applicationId,
254
+ after: body.views,
255
+ before: async () => original.views,
256
+ where: `layout '${String(current.name ?? id)}'`
257
+ }, policyOverride);
238
258
  // Gövde değil FARK gönderilir: gateway normalizasyonları (komponent varsayılanları, katalog
239
259
  // kuralları, value'suz olayların elenmesi) son hâle işlendikten sonra net diff hesaplanır.
240
260
  const finalDocument = { ...patched, name: body.name, parameters: body.parameters, views: body.views, events: body.events };
@@ -261,13 +281,13 @@ export function registerLayoutTools(server, platform) {
261
281
  summary.propTypes = propTypes.applied;
262
282
  if (gaps.length > 0)
263
283
  summary.layoutWarnings = gaps;
264
- return withNormalization(summary, [
284
+ return attachFindings(withNormalization(summary, [
265
285
  ...componentNameNote(named),
266
286
  ...componentDefaultsNote(fixed),
267
287
  ...catalogNotes,
268
288
  ...slotWarning(body.views),
269
289
  ...resolved.notes.map(note => `patch yolu seçiciden indekse çözüldü: ${note}`)
270
- ]);
290
+ ]), textFindings);
271
291
  }));
272
292
  server.registerTool('layout_delete', {
273
293
  title: 'Layout sil',
@@ -1,4 +1,4 @@
1
- import { destructive, guid, pagingParams, readOnly, updates, writes } from './common.js';
1
+ import { destructive, guid, pagingParams, readOnly, templateSubscriptionLock, updates, writes } from './common.js';
2
2
  import { handle } from '../platform/responses.js';
3
3
  import { z } from 'zod';
4
4
  const mergeView = z.enum(['summary', 'conflicts', 'full']).optional()
@@ -43,7 +43,7 @@ export function registerMergeTools(server, platform) {
43
43
  const config = platform.config;
44
44
  server.registerTool('merge_plan', {
45
45
  title: 'Merge planı oluştur',
46
- 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.",
46
+ 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. ${templateSubscriptionLock}`,
47
47
  inputSchema: { projectId: guid, sourceEnvironmentId: guid, title: mergeTitle, description: mergeDescription },
48
48
  annotations: writes
49
49
  }, handle(config, async ({ projectId, sourceEnvironmentId, title, description }) => {
@@ -61,7 +61,7 @@ export function registerMergeTools(server, platform) {
61
61
  }, handle(config, ({ projectId, ...query }) => platform.org('GET', `/merges/${projectId}`, { query })));
62
62
  server.registerTool('merge_get', {
63
63
  title: 'Merge planını oku',
64
- description: "Bir merge planını döner. items[] yalnız DEĞİŞEN kayıtları taşır; her kayıtta kind (add|update|delete|conflict) ve elements[] vardır. Çakışan elemanlarda base/source/target üç değeri birden gelir. dataRisk: true olan kalemde SEBEP dataRiskReasons[] içindedir: 'field-removed' (alan siliniyor), 'field-narrowed' (tip/nullable/unique daraltılıyor), 'not-null-without-default-on-populated-table' (hedefte satır varken varsayılansız NOT NULL alan ekleniyor — bu apply'ı DDL aşamasında düşürür, alanı nullable yapın ya da varsayılan verin), 'table-removed' (hedefte satırı olan tablo siliniyor). dataRisk bir UYARIDIR, apply'ı bloklamaz. items[].dependents[] ({ id, name, module }) dolu olan kalem, DALIN SİLDİĞİ ama development'ın dal açıldıktan sonra kullanmaya başladığı kayıttır: silme ÇAKIŞMAYA döner; hedef lehine çözüm kaydı ve bağımlısını korur, kaynak lehine çözüm siler (bağımlı FK ile düşebilir). Sorgunun çıktı DTO'su ve alanları aynı çakışma grubuna girer. Süreç kenarlarında (edges) iki taraf AYNI geçidin çıkışlarına dokunduysa yol 'versions[id=…].model.edges{from=<düğüm>}' olan TEK çakışma gelir; farklı geçitler kendiliğinden birleşir. Merge çevirileri (translation, adı 'key') ve microfunction'ları da taşır; microfunction hedefte TASLAK kalır, yayını kullanıcı yapar. derivedFromKey dolu olan kalem OTOMATİK ÜRETİLMİŞ bir kayıttır (varsayılan DTO); kaynağını (entity ya da query) çözdüğünüzde aynı yoldaki çakışması aynı tarafa KENDİLİĞİNDEN çözülür — ayrı ayrı ve zıt çözülemez. Uygulanmış planlarda title, description, createdBy ve appliedBy da gelir. kind=revert ise bu bir geri alma planıdır: revertsPlanId geri aldığı planı verir ve elements'te source merge ÖNCESİ, target ŞİMDİKİ değerdir. isStale: true ise plan alındığından beri DAL ya da development değişmiştir: items o değişikliği taşimaz ve merge_apply 409 döner — merge_refresh ile planı tazeleyin. elements[].isNoise: true olan eleman GÖSTERİM gürültüsüdür — kanvasın kendi durumu (workflow schema), DTO'dan türetilen şema (microservice schema.parameterScheme/returnScheme), sunucu yönetimli anahtar; merge gövdesinde KALIR ve uygulanır, yalnız okurken atlanır. Çakışan eleman asla işaretlenmez. items[].references[] o kalemin gövdesindeki guid'lerin karşılığıdır ({ id, name, module }): bir table step'in rule.entityId'si hangi tabloya, bir branch'in nextStepId'si hangi adıma, bir filtrenin fieldId'si hangi alana işaret ediyor — plan dışına çıkmadan okunur. items[].parentKey dolu olan kayıt başka bir kaydın çocuğudur (DTO çocukları, iç içe klasörler); ebeveyniyle birlikte okunur. items[].recordType kaydın modül içindeki türüdür — bir workflow kaydı 'workflow', 'automation' ya da 'middleware' olabilir ve modül üçüne de workflow der. items[].hiddenFields o kaydın GÖVDESİNDE okunmayacak anahtarlardır (aynı politika; eklenen/silinen kayıt tek eleman olarak geldiği için yolun değil anahtarın sorusudur). Bir taraf prop'un TİPİNİ (valueType: static → code) değiştirip öbür taraf da tipine ya da değerine (ref/data) dokunduysa eleman prop'un KENDİSİDİR: yol '…props[id=…]', base/source/target prop gövdesi, combinable: false — tip ile değer tek karardır. Nesne prop'unda (icon, surfaceStyle) anahtarın değeri '…ref.<anahtar>…', çizilen satırı '…children[name=<anahtar>]…' yolundadır; ikisi de çakışırsa kararı ref'e verin — satır karar istemez, unresolved'a sayılmaz; merge onu değerin birleşik hâlinden kurar. ÇÖZÜM DEĞER TAŞIR: elements[].combinable: true olan çakışma (kimliksiz ya da gruplu liste) both ile birleştirilebilir; elements[].value both/custom çözümünün yazılacak değeridir. kind='duplicate' OLASI KOPYADIR: iki taraf aynı içeriği farklı kimlikle eklemiş, twinPath development'taki ikizdir. combinable: true olan kopya ENGELLEMEZ — çözümsüzse ikisi de yazılır; kopyalar aynı slug'ı taşıyorsa (tablo alanı = kolon, sayfa = rota) combinable: false gelir, ikisi birden kalamaz: unresolved sayılır, both reddedilir, birini seçmek gerekir. Tablo alanında slug, bileşen prop'unda ad TEKİLDİR: içerik farklı olsa da aynı slug'lı alan ya da aynı adlı prop eklemesi combinable: false kopyadır; aynı ad farklı slug'lı iki alan ayrı eklenir (name-clash uyarısı). Development tabanda var olan bir alanı dalın eklediği slug'a yeniden adlandırdıysa dalın alanı yine duplicate gelir; base doludur (alanın taban hâli) ve source 400 döner — veri taşıyan alan silinmez, target ya da custom seçilir. target dalınkini düşürür, source development'ınkini çıkarır, custom tek öğe yazar; düşen kopyadan türemiş DTO alanı da düşer. summary.duplicates çözümsüz kopya sayısıdır. items[].derivedFromPath dolu olan DTO alanı tablo alanına bağlıdır (fields[id=…]); tablo alanı kararı ona kendiliğinden geçer. checks: true merge öncesi doğrulamayı da ekler: unresolved (error) · duplicate-open · name-clash (aynı adla iki kardeş) · dangling-reference (merge'ün düşürdüğü kimliğe kalan başvuru) — uyarılar engellemez, merge_apply'dan önce okunur.",
64
+ description: "Bir merge planını döner. items[] yalnız DEĞİŞEN kayıtları taşır; her kayıtta kind (add|update|delete|conflict) ve elements[] vardır. Çakışan elemanlarda base/source/target üç değeri birden gelir. dataRisk: true olan kalemde SEBEP dataRiskReasons[] içindedir: 'field-removed' (alan siliniyor), 'field-narrowed' (tip/nullable/unique daraltılıyor), 'not-null-without-default-on-populated-table' (hedefte satır varken varsayılansız NOT NULL alan ekleniyor — bu apply'ı DDL aşamasında düşürür, alanı nullable yapın ya da varsayılan verin), 'table-removed' (hedefte satırı olan tablo siliniyor). dataRisk bir UYARIDIR, apply'ı bloklamaz. items[].dependents[] ({ id, name, module }) dolu olan kalem, DALIN SİLDİĞİ ama development'ın dal açıldıktan sonra kullanmaya başladığı kayıttır: silme ÇAKIŞMAYA döner; hedef lehine çözüm kaydı ve bağımlısını korur, kaynak lehine çözüm siler (bağımlı FK ile düşebilir). Sorgunun çıktı DTO'su ve alanları aynı çakışma grubuna girer. Süreç kenarlarında (edges) iki taraf AYNI geçidin çıkışlarına dokunduysa yol 'versions[id=…].model.edges{from=<düğüm>}' olan TEK çakışma gelir; farklı geçitler kendiliğinden birleşir. Merge çevirileri (translation, adı 'key') ve microfunction'ları da taşır; microfunction hedefte TASLAK kalır, yayını kullanıcı yapar. Kayıt taşıma ayarı açık tabloların SATIRLARI da gelir: module='row' kalemi bir tablodur (recordId entity kimliği, name tablo adı), satır farkı 'rows.<anahtar>', hücre farkı 'rows.<anahtar>.<sütun>' yolundadır; yalnız dalın değişiklikleri kalem üretir, development'ın kendi satırları plana girmez. İki taraf tabanda olmayan aynı anahtarla farklı satır açtıysa 'rows.<anahtar>' yolunda base'i boş TEK çakışma gelir (kimlik çakışması) — ayrıntı rehberde (Tablo kayıtları). derivedFromKey dolu olan kalem OTOMATİK ÜRETİLMİŞ bir kayıttır (varsayılan DTO); kaynağını (entity ya da query) çözdüğünüzde aynı yoldaki çakışması aynı tarafa KENDİLİĞİNDEN çözülür — ayrı ayrı ve zıt çözülemez. Uygulanmış planlarda title, description, createdBy ve appliedBy da gelir. kind=revert ise bu bir geri alma planıdır: revertsPlanId geri aldığı planı verir ve elements'te source merge ÖNCESİ, target ŞİMDİKİ değerdir. isStale: true ise plan alındığından beri DAL ya da development değişmiştir: items o değişikliği taşimaz ve merge_apply 409 döner — merge_refresh ile planı tazeleyin. elements[].isNoise: true olan eleman GÖSTERİM gürültüsüdür — kanvasın kendi durumu (workflow schema), DTO'dan türetilen şema (microservice schema.parameterScheme/returnScheme), sunucu yönetimli anahtar; merge gövdesinde KALIR ve uygulanır, yalnız okurken atlanır. Çakışan eleman asla işaretlenmez. items[].references[] o kalemin gövdesindeki guid'lerin karşılığıdır ({ id, name, module }): bir table step'in rule.entityId'si hangi tabloya, bir branch'in nextStepId'si hangi adıma, bir filtrenin fieldId'si hangi alana işaret ediyor — plan dışına çıkmadan okunur. items[].parentKey dolu olan kayıt başka bir kaydın çocuğudur (DTO çocukları, iç içe klasörler); ebeveyniyle birlikte okunur. items[].recordType kaydın modül içindeki türüdür — bir workflow kaydı 'workflow', 'automation' ya da 'middleware' olabilir ve modül üçüne de workflow der. items[].hiddenFields o kaydın GÖVDESİNDE okunmayacak anahtarlardır (aynı politika; eklenen/silinen kayıt tek eleman olarak geldiği için yolun değil anahtarın sorusudur). Bir taraf prop'un TİPİNİ (valueType: static → code) değiştirip öbür taraf da tipine ya da değerine (ref/data) dokunduysa eleman prop'un KENDİSİDİR: yol '…props[id=…]', base/source/target prop gövdesi, combinable: false — tip ile değer tek karardır. Nesne prop'unda (icon, surfaceStyle) anahtarın değeri '…ref.<anahtar>…', çizilen satırı '…children[name=<anahtar>]…' yolundadır; ikisi de çakışırsa kararı ref'e verin — satır karar istemez, unresolved'a sayılmaz; merge onu değerin birleşik hâlinden kurar. ÇÖZÜM DEĞER TAŞIR: elements[].combinable: true olan çakışma (kimliksiz ya da gruplu liste) both ile birleştirilebilir; elements[].value both/custom çözümünün yazılacak değeridir. kind='duplicate' OLASI KOPYADIR: iki taraf aynı içeriği farklı kimlikle eklemiş, twinPath development'taki ikizdir. combinable: true olan kopya ENGELLEMEZ — çözümsüzse ikisi de yazılır; kopyalar aynı slug'ı taşıyorsa (tablo alanı = kolon, sayfa = rota) combinable: false gelir, ikisi birden kalamaz: unresolved sayılır, both reddedilir, birini seçmek gerekir. Tablo alanında slug, bileşen prop'unda ad TEKİLDİR: içerik farklı olsa da aynı slug'lı alan ya da aynı adlı prop eklemesi combinable: false kopyadır; aynı ad farklı slug'lı iki alan ayrı eklenir (name-clash uyarısı). Development tabanda var olan bir alanı dalın eklediği slug'a yeniden adlandırdıysa dalın alanı yine duplicate gelir; base doludur (alanın taban hâli) ve source 400 döner — veri taşıyan alan silinmez, target ya da custom seçilir. target dalınkini düşürür, source development'ınkini çıkarır, custom tek öğe yazar; düşen kopyadan türemiş DTO alanı da düşer. summary.duplicates çözümsüz kopya sayısıdır. items[].derivedFromPath dolu olan DTO alanı tablo alanına bağlıdır (fields[id=…]); tablo alanı kararı ona kendiliğinden geçer. checks: true merge öncesi doğrulamayı da ekler: unresolved (error) · not-applicable (error: kararın yazılacağı yer development'ta yok — apply düşer; merge_refresh edip o yolda yeniden karar ver) · duplicate-open · name-clash (aynı adla iki kardeş) · dangling-reference (merge'ün düşürdüğü kimliğe kalan başvuru) — uyarılar engellemez, merge_apply'dan önce okunur.",
65
65
  inputSchema: {
66
66
  projectId: guid,
67
67
  planId: guid,
@@ -81,7 +81,7 @@ export function registerMergeTools(server, platform) {
81
81
  }));
82
82
  server.registerTool('merge_resolve', {
83
83
  title: 'Merge çakışmalarını çöz',
84
- description: "Çakışan elemanlar ve olası kopyalar için kararı plana yazar: source (dal), target (development), both (iki tarafın birleşimi — yalnız combinable: true elemanda; kopyada ikisini de tut) ya da custom (value'daki değer). Yalnız status=planned iken çalışır. path boş bırakılırsa o kaydın tüm çakışmaları aynı karara çözülür. Türetilmiş kaydı (varsayılan DTO) kaynağına zıt çözmek 400 döner. YANIT ÖZETTİR: { planId, status, summary, resolved } — platform planın tamamını döner ama gateway onu kesip yalnız sayaçları verir (orta boy planda tam gövde 68-121 KB'a çıkıyor ve yanıt sınırını aşıyordu; çağrı hata gibi görünürken çözüm UYGULANMIŞ oluyordu). Planın kendisini görmek için merge_get { planId, view:'conflicts' } çağırın; kalan çakışma sayısı summary.unresolved'dadır.",
84
+ description: `Çakışan elemanlar ve olası kopyalar için kararı plana yazar: source (dal), target (development), both (iki tarafın birleşimi — yalnız combinable: true elemanda; kopyada ikisini de tut; tablo satırının kimlik çakışmasında development'ın satırı kalır, dalın satırı YENİ ANAHTARLA eklenir, metin anahtarda 400) ya da custom (value'daki değer). Yalnız status=planned iken çalışır. path boş bırakılırsa o kaydın tüm çakışmaları aynı karara çözülür. Türetilmiş kaydı (varsayılan DTO) kaynağına zıt çözmek 400 döner. YANIT ÖZETTİR: { planId, status, summary, resolved } — platform planın tamamını döner ama gateway onu kesip yalnız sayaçları verir (orta boy planda tam gövde 68-121 KB'a çıkıyor ve yanıt sınırını aşıyordu; çağrı hata gibi görünürken çözüm UYGULANMIŞ oluyordu). Planın kendisini görmek için merge_get { planId, view:'conflicts' } çağırın; kalan çakışma sayısı summary.unresolved'dadır. ${templateSubscriptionLock}`,
85
85
  inputSchema: { projectId: guid, planId: guid, resolutions: z.array(resolution).min(1) },
86
86
  annotations: updates
87
87
  }, handle(config, async ({ projectId, planId, resolutions }) => {
@@ -90,7 +90,7 @@ export function registerMergeTools(server, platform) {
90
90
  }));
91
91
  server.registerTool('merge_apply', {
92
92
  title: 'Merge planını uygula',
93
- description: "Planı tek transaction'da development'a uygular ve dalın şemasını düşürür. İsteğin adı zaten plan açılırken verilir; apply ayrı bir mesaj almaz. Asenkron: yanıt 202 döner. Çözülmemiş çakışma varsa 409 döner. PLAN BAYATSA DA 409 döner: plan alındıktan sonra DAL ya da development değiştiyse apply reddedilir — plan dalın o anki fotoğrafını taşır, uygulansaydı sonradan yapılan iş sessizce düşerdi. Çözümü merge_refresh'tir; yeni plan açmanız gerekmez.",
93
+ description: `Planı tek transaction'da development'a uygular ve dalın şemasını düşürür. İsteğin adı zaten plan açılırken verilir; apply ayrı bir mesaj almaz. Asenkron: yanıt 202 döner. Çözülmemiş çakışma varsa 409 döner. PLAN BAYATSA DA 409 döner: plan alındıktan sonra DAL ya da development değiştiyse apply reddedilir — plan dalın o anki fotoğrafını taşır, uygulansaydı sonradan yapılan iş sessizce düşerdi. Çözümü merge_refresh'tir; yeni plan açmanız gerekmez. ${templateSubscriptionLock}`,
94
94
  inputSchema: { projectId: guid, planId: guid },
95
95
  annotations: writes
96
96
  }, handle(config, async ({ projectId, planId }) => {
@@ -102,7 +102,7 @@ export function registerMergeTools(server, platform) {
102
102
  }));
103
103
  server.registerTool('merge_refresh', {
104
104
  title: 'Merge planını tazele',
105
- description: "Açık bir planı dalın ve development'ın ŞU ANKİ hâline göre yeniden hesaplatır. İstek yerinde kalır: id'si, başlığı, açıklaması ve geçmişi korunur — yalnız değişiklik listesi yenilenir. DEĞİŞMEYEN ELEMANIN KARARI KORUNUR: aynı kalem, aynı yol ve üç tarafı (base/source/target) aynı kalan elemanın kararı ve değeri yeni plana taşınır; değişenler yeniden sorulur (etkinlikte merge.resolutions_carried { carried, dropped }). Yalnız status=planned bir planda çalışır ve dal ready olmalıdır. Bayat bir planı (merge_get → isStale: true, ya da merge_apply 409) düzeltmenin yolu budur; merge_discard + merge_plan istemez. Asenkron: yanıt 202, plan planning durumundadır — merge_get ile planned bekleyin.",
105
+ description: `Açık bir planı dalın ve development'ın ŞU ANKİ hâline göre yeniden hesaplatır. İstek yerinde kalır: id'si, başlığı, açıklaması ve geçmişi korunur — yalnız değişiklik listesi yenilenir. DEĞİŞMEYEN ELEMANIN KARARI KORUNUR: aynı kalem, aynı yol ve üç tarafı (base/source/target) aynı kalan elemanın kararı ve değeri yeni plana taşınır; değişenler yeniden sorulur (etkinlikte merge.resolutions_carried { carried, dropped }). Yalnız status=planned bir planda çalışır ve dal ready olmalıdır. Bayat bir planı (merge_get → isStale: true, ya da merge_apply 409) düzeltmenin yolu budur; merge_discard + merge_plan istemez. Asenkron: yanıt 202, plan planning durumundadır — merge_get ile planned bekleyin. ${templateSubscriptionLock}`,
106
106
  inputSchema: { projectId: guid, planId: guid },
107
107
  annotations: writes
108
108
  }, handle(config, async ({ projectId, planId }) => {
@@ -114,7 +114,7 @@ export function registerMergeTools(server, platform) {
114
114
  }));
115
115
  server.registerTool('merge_revert', {
116
116
  title: "Uygulanmış merge'i geri al",
117
- description: "Uygulanmış bir merge'i geri alan YENİ bir plan açar; kaynak plan silinmez, reverted olur ve geçmişte kalır. Yalnız status=applied|applied-with-drift ve kind=merge planlar geri alınabilir, pencere RevertWindowDays (varsayılan 7 gün) — dışında 400 döner. Ters plan kaynak plandaki her işlemi tersine çevirir: eklenen kayıt silinir, silinen geri gelir, değişen eski gövdesine döner. Merge'den SONRA elle değiştirilen kayıtlar conflict gelir ve orada source = merge öncesi değer, target = şimdiki değer demektir. Asenkron: yanıt 202, ters plan planning durumundadır.",
117
+ description: `Uygulanmış bir merge'i geri alan YENİ bir plan açar; kaynak plan silinmez, reverted olur ve geçmişte kalır. Yalnız status=applied|applied-with-drift ve kind=merge planlar geri alınabilir, pencere RevertWindowDays (varsayılan 7 gün) — dışında 400 döner. Ters plan kaynak plandaki her işlemi tersine çevirir: eklenen kayıt silinir, silinen geri gelir, değişen eski gövdesine döner; tablo satırları (module='row') da aynı kuralla döner. Merge'den SONRA elle değiştirilen kayıtlar conflict gelir ve orada source = merge öncesi değer, target = şimdiki değer demektir. Asenkron: yanıt 202, ters plan planning durumundadır. ${templateSubscriptionLock}`,
118
118
  inputSchema: { projectId: guid, planId: guid },
119
119
  annotations: writes
120
120
  }, handle(config, async ({ projectId, planId }) => {
@@ -126,7 +126,7 @@ export function registerMergeTools(server, platform) {
126
126
  }));
127
127
  server.registerTool('merge_discard', {
128
128
  title: 'Merge planından vazgeç',
129
- description: "Planı uygulamadan kapatır. Uygulanmakta olan (status=applying) bir plan bırakılamaz — 400 döner. Dal ve şeması olduğu gibi kalır.",
129
+ description: `Planı uygulamadan kapatır. Uygulanmakta olan (status=applying) bir plan bırakılamaz — 400 döner. Dal ve şeması olduğu gibi kalır. ${templateSubscriptionLock}`,
130
130
  inputSchema: { projectId: guid, planId: guid },
131
131
  annotations: destructive
132
132
  }, handle(config, ({ projectId, planId }) => platform.org('DELETE', `/merges/${projectId}/${planId}`)));
@@ -2,7 +2,7 @@ import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, u
2
2
  import { constraintSummary } from '../knowledge/backendConstraints.js';
3
3
  import { unmappedMembers, withUnmappedMembers } from '../knowledge/schemaGuard.js';
4
4
  import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
5
- import { policyRules } from '../knowledge/rules.js';
5
+ import { policyRules, microfunctionArchiveEntrypointFindings } from '../knowledge/rules.js';
6
6
  import { handle } from '../platform/responses.js';
7
7
  import { z } from 'zod';
8
8
  const policyOverrideParam = z.boolean().optional()
@@ -19,7 +19,7 @@ const definitionInput = {
19
19
  language: z.enum(['node', 'python']).describe("Çalışma dili. Inline kaynakta dosya adı sabittir: node → function.js (exports.handler = async (args, context) => …), python → function.py (def handler(args, context))"),
20
20
  provider: z.enum(['built-in', 'aws-lambda']).describe("'built-in' platformun kendi Fission kümesinde koşar ve runtime hesabı İSTEMEZ (runtimeId boş kalır); 'aws-lambda' müşterinin kendi hesabında koşar ve runtimeId ZORUNLUDUR. 'azure-functions' enum'da tanımlı ama v1'de reddedilir (Faz 2)"),
21
21
  runtimeId: guid.optional().describe("AWS Lambda hesabı (microfunction_runtime_list). provider 'built-in' ise VERİLMEZ; 'aws-lambda' ise zorunlu ve hesabın provider'ı fonksiyonunkiyle aynı olmalı"),
22
- sourceKind: z.enum(['inline', 'archive']).describe("'inline' ⇒ code zorunlu (≤ 512 KB, standart kütüphane; bağımlılık YOK); 'archive' ⇒ önce kayıt açılır, zip Studio'dan yüklenir (MCP'de zip yükleme yok — Faz 2) ve entrypoint 'modül.fonksiyon' olarak verilir"),
22
+ sourceKind: z.enum(['inline', 'archive']).describe("'inline' ⇒ code zorunlu (≤ 512 KB); dış paketler dependencies ile bildirilir. 'archive' ⇒ entrypoint 'modül.fonksiyon' olarak verilir; dosyalar zip'in içinde yaşar ve microfunction_archive_scaffold (iskelet) + microfunction_files_apply ile yazılır — paketler zip'teki requirements.txt / package.json'dadır, dependencies yok sayılır. Tek parça zip yükleme MCP'de yok (Studio'dan)"),
23
23
  code: z.string().optional().describe("Inline kaynak kodu. Sözleşme: tek bir handler dışa aktarılır, args çağrı argümanları, context SALT OKUNUR anlık görüntüdür (input/variables/steps/user/request/language/project/environment). Yazma yalnız dönüş değeriyle olur. Ayrıntı: platform_guide('microfunction')"),
24
24
  entrypoint: z.string().optional().describe("Zip kaynağında 'modül.fonksiyon' (ör. 'handlers/sum.handler'). Inline kaynakta gateway'in de platformun da beklediği sabit 'function.handler'dır — vermeyin"),
25
25
  parameterDtoId: guid.optional().describe("Çağrı argümanlarının sözleşmesi (DataTransferObject). Workflow step'i ifadeyi bu şemaya göre yazar; boşsa args = {} gider"),
@@ -29,8 +29,17 @@ const definitionInput = {
29
29
  timeoutSeconds: z.number().int().min(1).max(900).optional().describe("Fonksiyon bütçesi; varsayılan 30. Built-In üst sınırı 300, Lambda 900. Bütçe dolarsa çağrı MicrofunctionInvocationTimeoutException ile step'in fail dalına düşer"),
30
30
  memoryMb: z.number().int().min(128).max(3008).optional().describe("Varsayılan 256. Built-In'de yalnız kaydedilir (küme environment limiti geçerlidir); Lambda'da gerçekten uygulanır"),
31
31
  environmentVariables: environmentVariablesParam.optional(),
32
+ dependencies: z.string().optional().describe("Inline kaynağın dış paketleri, SATIR BAŞINA BİR PAKET (≤ 16 KB; boş ve '#' satırları yorum). python: requirements.txt satırı olduğu gibi — 'pillow==11.1.0', 'requests>=2.32'; pip seçeneği ('-r', '--index-url', '-e') YASAK. node: 'ad@aralık' — 'sharp@^0.33.0', '@scope/ad@1.2', aralıksız 'dayjs' → latest. Built-In'de yayın paketleri Fission builder'da kurar: yayın build süresince (5 dk'ya kadar) 'publishing' kalır, kurulum düşerse publish-failed ve publishError'da build günlüğünün son 4 KB'ı. aws-lambda'da KURULMAZ (MF-007) — paketler zip'te gelmeli. archive kaynağında yok sayılır (manifest zip'te). Built-In Python 3.13, Node 22 (Lambda'daki sürümlerle aynı); C uzantılı paketler (Pillow gibi) hazır wheel'den kurulur"),
33
+ testArguments: z.string().optional().describe("Studio Test call'unun kayıtlı argüman metni (≤ 64 KB, MicrofunctionTestArgumentsTooLarge); olduğu gibi saklanır, JSON geçerliliği DENETLENMEZ ve koşuma girmez. update TAM GÖVDEDİR: verilmezse kayıtlı metin silinir — önce microfunction_get'ten alın. Kaydetmek revision'ı artırır (yayındaki kayıt hasUnpublishedChanges: true olur)"),
32
34
  publish: z.boolean().optional().describe("true: kayıt açıldıktan/güncellendikten sonra aynı çağrıda yayın istenir ('Kaydet ve yayınla'). Yayın ASENKRONDUR — çağrı status: 'publishing' döner; sonucu microfunction_get ile izleyin (published | publish-failed + publishError)")
33
35
  };
36
+ const archiveOperationInput = z.object({
37
+ type: z.enum(['write-file', 'create-directory', 'delete', 'rename']).describe("write-file: dosyayı oluşturur ya da içeriğini değiştirir · create-directory: boş klasör · delete: dosya ya da klasör (altındaki HER girdiyle) · rename: dosya ya da klasörü newPath'e taşır (klasörse altındakilerle)"),
38
+ path: z.string().min(1).describe("Zip içindeki yol, '/' ayraçlı: 'main.js', 'lib/util.js'. Kökle (/) başlamaz, / ile bitmez; boş, '.' ve '..' segmenti ve ters bölü içermez. Klasör yolu da sondaki / OLMADAN yazılır ('lib')"),
39
+ content: z.string().optional().describe("write-file için zorunlu. Metin dosyasında düz metin; ikili dosyada base64 + encoding: 'base64' (MCP'den tek dosya ≤ 8 MB)"),
40
+ encoding: z.enum(['utf8', 'base64']).optional().describe("content'in kodlaması; varsayılan utf8"),
41
+ newPath: z.string().optional().describe('rename için zorunlu hedef yol; path ile aynı kurallar. Hedef dolu olamaz, klasör kendi altına taşınamaz')
42
+ });
34
43
  const runtimeInput = {
35
44
  name: z.string().min(1).max(255).describe('Hesap adı; benzersiz'),
36
45
  provider: z.literal('aws-lambda').optional().describe("v1'de yalnız 'aws-lambda' kabul edilir; gönderilmezse gateway onu koyar"),
@@ -101,11 +110,52 @@ export function registerMicrofunctionTools(server, platform) {
101
110
  project: projectParam,
102
111
  environment: environmentParam,
103
112
  id: guid,
104
- args: z.record(z.unknown()).optional().describe("Fonksiyona giden argüman nesnesi (handler'ın ilk parametresi); parameterDtoId varsa o şemaya uymalı"),
113
+ args: z.unknown().optional().describe("Handler'ın ilk parametresi; herhangi bir JSON değeri. parameterBehavior 'object' ⇒ nesne, 'array' ⇒ dizi, 'primitive' ⇒ tek değer (workflow step'inin gönderdiği şekille aynı). parameterDtoId varsa o şemaya uymalı; boşsa {} gider"),
105
114
  preview: z.boolean().optional().describe("true: shim logları (console.log/print) zarfın logs alanında döner. false/boş: logs boş gelir — hata ayıklarken açın")
106
115
  },
107
116
  annotations: updates
108
117
  }, handle(config, ({ project, environment, id, args, preview }) => platform.tenant('POST', project, `/microfunctions/${id}/invoke`, { environment, body: { args, preview } })));
118
+ server.registerTool('microfunction_files_list', {
119
+ title: 'Microfunction arşiv dosyalarını listele',
120
+ description: "Zip kaynaklı microfunction'ın arşivindeki dosya ve klasörleri listeler: { hash, entries: [{ path, size, isDirectory }] }. Yalnız zip'te açık girdisi olan klasörler listelenir; ara klasörler dosya yollarından çıkar. microfunction_files_apply'a vereceğiniz expectedHash BURADAN gelir — yazmadan önce bu tool'u çağırın. Arşiv yoksa hash null ve liste boştur: sıfırdan başlamak için microfunction_archive_scaffold.",
121
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid },
122
+ annotations: readOnly
123
+ }, handle(config, ({ project, environment, id }) => platform.tenant('GET', project, `/microfunctions/${id}/archive/entries`, { environment })));
124
+ server.registerTool('microfunction_file_get', {
125
+ title: 'Microfunction arşiv dosyasını oku',
126
+ description: "Arşivdeki tek dosyanın içeriğini döner: { path, size, isBinary, content }. 1 MB'ı aşan ya da UTF-8 metin olmayan dosyada isBinary: true ve content boştur — içeriği görmek için zip'in tamamı Studio'dan indirilir. Yol yoksa ya da klasörse 404.",
127
+ inputSchema: {
128
+ project: projectParam,
129
+ environment: environmentParam,
130
+ id: guid,
131
+ path: z.string().min(1).describe("Zip içindeki dosya yolu, microfunction_files_list'teki path ile aynı: 'lib/util.js'")
132
+ },
133
+ annotations: readOnly
134
+ }, handle(config, ({ project, environment, id, path }) => platform.tenant('GET', project, `/microfunctions/${id}/archive/file`, { environment, query: { path } })));
135
+ server.registerTool('microfunction_files_apply', {
136
+ title: 'Microfunction arşiv dosyalarını değiştir',
137
+ description: "Arşive bir dizi dosya işlemini TEK seferde uygular; platform yeni zip üretir, kayıt yeni archiveHash alır, revision artar ve eski zip nesnesi silinir. İşlemler SIRAYLA uygulanır. expectedHash microfunction_files_list'ten okunan hash'tir: kayıt o arada değiştiyse 409 (MicrofunctionArchiveConflictException) döner ve hiçbir şey yazılmaz — listeyi yeniden okuyup işlemleri ona göre kurun. Arşivi olmayan kayıtta expectedHash VERİLMEZ. Kayıt yayındaysa runtime eski sürümü koşmaya devam eder: yeni kod için microfunction_publish; yayın, entrypoint modülü zip'te yoksa 400 döner. " + constraintSummary('ApplyMicrofunctionArchiveChangesCommand'),
138
+ inputSchema: {
139
+ project: projectParam,
140
+ environment: environmentParam,
141
+ id: guid,
142
+ expectedHash: z.string().optional().describe("microfunction_files_list'in döndürdüğü hash; arşiv yoksa verilmez"),
143
+ operations: z.array(archiveOperationInput).describe('Sırayla uygulanan işlemler; en az 1'),
144
+ policyOverride: policyOverrideParam
145
+ },
146
+ annotations: updates
147
+ }, handle(config, async ({ project, environment, id, policyOverride, ...body }) => {
148
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'microfunctionArchive', body, { operation: 'update' }, policyOverride));
149
+ const entrypointFindings = await microfunctionArchiveEntrypointFindings(platform, project, environment, id, body.operations);
150
+ const drift = unmappedMembers('ApplyMicrofunctionArchiveChangesCommand', body);
151
+ return attachFindings(withUnmappedMembers(await platform.tenant('POST', project, `/microfunctions/${id}/archive/changes`, { environment, body }), drift), [...findings, ...entrypointFindings]);
152
+ }));
153
+ server.registerTool('microfunction_archive_scaffold', {
154
+ title: 'Microfunction arşiv iskeleti oluştur',
155
+ description: "Arşivi olmayan kayda dilin iskelet dosyalarını yazar ve kaydı zip kaynağına çevirir: node → main.js + package.json (boş dependencies), python → main.py + requirements.txt (yalnız yorum); entrypoint ikisinde de 'main.handler' olur. Paketler bu manifest dosyalarına yazılır (microfunction_files_apply) ve Built-In yayında kurulur. Arşiv zaten varsa 409 — mevcut dosyaları microfunction_files_list ile okuyun. Node'da giriş modülü index.js OLAMAZ: index.js platform shim'inin adıdır ve yayın bunu 400 ile reddeder.",
156
+ inputSchema: { project: projectParam, environment: environmentParam, id: guid },
157
+ annotations: writes
158
+ }, handle(config, ({ project, environment, id }) => platform.tenant('POST', project, `/microfunctions/${id}/archive/scaffold`, { environment })));
109
159
  server.registerTool('microfunction_runtime_list', {
110
160
  title: 'Microfunction runtime hesaplarını listele',
111
161
  description: "Bulut runtime hesaplarını (v1: AWS Lambda) listeler; secretAccessKey maskeli döner. Built-In fonksiyonların runtime kaydı YOKTUR — liste boşsa yalnız provider: 'built-in' fonksiyon tanımlayabilirsiniz.",