@appaflytech/zeyrek-ai-mcp 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +21 -7
- package/.env.production.example +42 -0
- package/README.md +14 -7
- package/dist/config.js +113 -46
- package/dist/knowledge/componentCatalog.js +476 -32
- package/dist/knowledge/componentEvents.js +154 -12
- package/dist/knowledge/errorPath.js +64 -0
- package/dist/knowledge/normalize.js +12 -1
- package/dist/knowledge/outlines.js +9 -2
- package/dist/knowledge/rules.js +184 -3
- package/dist/knowledge/styleResolve.js +485 -0
- package/dist/observability/trace.js +4 -0
- package/dist/platform/http.js +3 -0
- package/dist/platform/responses.js +80 -4
- package/dist/platform/screenshot.js +106 -0
- package/dist/resources/examples/layout-app-shell.json +368 -0
- package/dist/resources/examples/microservice-table-list.json +1 -0
- package/dist/resources/examples/middleware-authenticate.json +243 -47
- package/dist/resources/examples/workflow-basic.json +24 -2
- package/dist/resources/guides/component.md +6 -3
- package/dist/resources/guides/entity.md +33 -1
- package/dist/resources/guides/external-component.md +129 -0
- package/dist/resources/guides/layout.md +147 -0
- package/dist/resources/guides/microservice.md +35 -0
- package/dist/resources/guides/middleware.md +10 -0
- package/dist/resources/guides/page.md +137 -15
- package/dist/resources/guides/translation.md +80 -0
- package/dist/resources/guides/workflow.md +127 -2
- package/dist/server.js +4 -0
- package/dist/tools/externalComponents.js +285 -0
- package/dist/tools/layouts.js +48 -6
- package/dist/tools/microservices.js +2 -2
- package/dist/tools/pages.js +98 -30
- package/dist/tools/platformInfo.js +2 -2
- package/dist/tools/projects.js +7 -2
- package/dist/tools/screens.js +36 -0
- package/dist/tools/translations.js +37 -0
- package/dist/tools/workflows.js +3 -3
- package/package.json +4 -1
|
@@ -119,7 +119,7 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
119
119
|
}));
|
|
120
120
|
server.registerTool('microservice_create', {
|
|
121
121
|
title: 'Microservice oluştur',
|
|
122
|
-
description: "Yeni microservice tanımlar (definition = CreateMicroserviceCommand). İlk kullanımdan önce platform_guide('microservice') okuyun: kaynak seçimi (table > query) ve selectFields disiplini. Auth/oturum gerektiren uçlarda definition.middlewareIds ile middleware bağlayın; kimlik kapsaması filtrede source:'expression' + value:'user.id' ile yapılır (platform_guide('middleware')). Gövde politika kontrolünden geçer.",
|
|
122
|
+
description: "Yeni microservice tanımlar (definition = CreateMicroserviceCommand). İlk kullanımdan önce platform_guide('microservice') okuyun: kaynak seçimi (table > query) ve selectFields disiplini. 'description' ZORUNLUDUR (MS-014, block): çağıranın bu uçtan ne elde ettiğini yazın. Auth/oturum gerektiren uçlarda definition.middlewareIds ile middleware bağlayın; kimlik kapsaması filtrede source:'expression' + value:'user.id' ile yapılır (platform_guide('middleware')). Ucun gerçek bir tuzağı varsa (auth istemiyor, sayfalama yok, atomik değil) 'caution' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın. Gövde politika kontrolünden geçer.",
|
|
123
123
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
124
124
|
annotations: writes
|
|
125
125
|
}, handle(config, async ({ project, environment, applicationId, definition, policyOverride }) => {
|
|
@@ -130,7 +130,7 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
130
130
|
}));
|
|
131
131
|
server.registerTool('microservice_update', {
|
|
132
132
|
title: 'Microservice güncelle',
|
|
133
|
-
description: 'Microservice tanımını günceller (tam gövde). Hedefli değişiklik için microservice_patch tercih edin. Gövde politika kontrolünden geçer.',
|
|
133
|
+
description: 'Microservice tanımını günceller (tam gövde). Hedefli değişiklik için microservice_patch tercih edin. \'description\' ZORUNLUDUR (MS-014, block). Ucun gerçek bir tuzağı varsa (auth istemiyor, sayfalama yok, atomik değil) \'caution\' alanına yazın — OPSİYONELDİR, tuzak yoksa boş bırakın. Gövde politika kontrolünden geçer.',
|
|
134
134
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
135
135
|
annotations: updates
|
|
136
136
|
}, handle(config, async ({ project, environment, applicationId, id, definition, policyOverride }) => {
|
package/dist/tools/pages.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
2
2
|
import { orphanedFlowWarnings, normalizeComponentDefaults, normalizeSchemaOps, schemaOpValidationErrors, validationNote, componentDefaultsNote, replacePropNote, ensurePageContainer, retargetRootAdds, rootAddsNote, blockSizeNote, heightTypeNote, reorderSchemaBlocks, reorderNote, nestedRepeaterNodes, nestedRepeaterMessage, stringStyleNodes, stringStyleWarning, inertFlexPropNodes, inertFlexPropWarning, FLEX_LAYOUT_PROP_NAMES, resetPageContainerPosition, pageContainerPositionWarning, verticalGapWarnings, invalidScreenEventNames, invalidWidgetTypeNodes, withNormalization, PAGE_CONTAINER_ELEMENT_ID } from '../knowledge/normalize.js';
|
|
3
|
-
import { fetchHost, pageUpdateBody, schemaParam, eventsParam, unwrap } from './screens.js';
|
|
3
|
+
import { fetchHost, pageUpdateBody, schemaParam, eventsParam, unwrap, modeParam, summarize, findNode } from './screens.js';
|
|
4
4
|
import { applyCatalogLayoutRules, fetchComponentEventNames } from './components.js';
|
|
5
5
|
import { formBindingWarnings } from '../knowledge/coverage.js';
|
|
6
6
|
import { PlatformApiError } from '../platform/errors.js';
|
|
7
|
-
import { handle } from '../platform/responses.js';
|
|
7
|
+
import { handle, toolResult, toolError } from '../platform/responses.js';
|
|
8
|
+
import { StudioScreenshotter } from '../platform/screenshot.js';
|
|
9
|
+
import { writeFile } from 'node:fs/promises';
|
|
10
|
+
import { resolve } from 'node:path';
|
|
8
11
|
import { z } from 'zod';
|
|
9
12
|
const opsParam = z.array(z.record(z.unknown())).describe("Micro-op listesi. Op sözlüğü: replace_prop {target, prop, value} | set_binding {target, prop, expression: '{{ Page.variables.x }}'} | add_component {parent (üst elementId ya da 'root'), index?, component} | remove_component {target} | move_component {target, parent, index?, position: {x, y}} | set_event {target, event, eventValue} | remove_event {target, event} (gateway emülasyonu: platformun micro-op'u olay SİLEMEZ, eventValue: null yalnız değersiz ölü kayıt bırakır — bu op şemayı okuyup olayı diziden çıkarır ve sayfayı tam gövdeyle geri yazar) | set_validations {target, validations: [{type, message, value, isActive}]} (komponentin validation listesini TAMAMEN değiştirir; boş dizi hepsini siler). target = elementId.");
|
|
10
|
-
const modeParam = z.enum(['summary', 'subtree', 'full']).optional().describe("summary (varsayılan): yalnız id/elementId/name/type ağacı — hedef bulmak için bunu kullanın. subtree: componentId ile tek alt ağaç. full: tüm şema.");
|
|
11
13
|
/**
|
|
12
14
|
* PAGE-008 — `set_event` uydurma bir olay adıyla 200 döner ve bağ SESSİZCE ölü kalır: motor olayı
|
|
13
15
|
* komponentin yaydığı adla eşleştirir, eşleşme yoksa akış hiç çalışmaz. Kontrol yalnız hedefin
|
|
@@ -46,28 +48,6 @@ function invalidComponentNames(nodes) {
|
|
|
46
48
|
}
|
|
47
49
|
return found;
|
|
48
50
|
}
|
|
49
|
-
function summarize(node) {
|
|
50
|
-
const summary = {
|
|
51
|
-
id: node.id,
|
|
52
|
-
elementId: node.elementId,
|
|
53
|
-
name: node.name,
|
|
54
|
-
type: node.type
|
|
55
|
-
};
|
|
56
|
-
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
57
|
-
summary.children = node.children.map(summarize);
|
|
58
|
-
}
|
|
59
|
-
return summary;
|
|
60
|
-
}
|
|
61
|
-
function findNode(nodes, target) {
|
|
62
|
-
for (const node of nodes ?? []) {
|
|
63
|
-
if (node.elementId === target || String(node.id) === target)
|
|
64
|
-
return node;
|
|
65
|
-
const found = findNode(node.children, target);
|
|
66
|
-
if (found)
|
|
67
|
-
return found;
|
|
68
|
-
}
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
51
|
function removeNodeFromTree(nodes, target) {
|
|
72
52
|
for (let index = 0; index < nodes.length; index++) {
|
|
73
53
|
if (nodes[index].elementId === target)
|
|
@@ -235,10 +215,11 @@ export function registerPageTools(server, platform) {
|
|
|
235
215
|
applicationSlug: z.string().min(1).describe("Uygulamanın slug'ı (application_list / project_overview)"),
|
|
236
216
|
pageSlug: z.string().min(1).describe("Sayfanın slug'ı (page_list)"),
|
|
237
217
|
view: z.enum(['summary', 'full']).optional().describe('summary (varsayılan): şema/layout ağaçları id/name/type özetine indirgenir. full: ham gövde — büyük olabilir'),
|
|
218
|
+
resolveStyles: z.boolean().optional().describe("summary görünümünde her düğüme EFEKTİF stilini ekler: tema componentStyles → preset → düğümün kendi stili kaskadı çözülür (aibox'ta tipli stil grupları da derlenir ve TABAN katman olur). styleLayers hangi katmanın konuştuğunu söyler — 'bu kutu neden böyle görünüyor' sorusunun cevabı budur."),
|
|
238
219
|
anonymous: z.boolean().optional().describe("true: istek HİÇBİR Authorization header'ı olmadan gider — 'korumalı sayfa tokensız ne döner, şema sızıyor mu?' sorusunun cevabı budur. Korumalı sayfada middlewareBlocked bekleyin.")
|
|
239
220
|
},
|
|
240
221
|
annotations: readOnly
|
|
241
|
-
}, handle(config, async ({ project, environment, applicationSlug, pageSlug, view, anonymous }) => {
|
|
222
|
+
}, handle(config, async ({ project, environment, applicationSlug, pageSlug, view, resolveStyles, anonymous }) => {
|
|
242
223
|
let response;
|
|
243
224
|
try {
|
|
244
225
|
response = await platform.tenant('GET', project, `/${applicationSlug}/p/${pageSlug}`, { environment, anonymous });
|
|
@@ -258,20 +239,107 @@ export function registerPageTools(server, platform) {
|
|
|
258
239
|
return response;
|
|
259
240
|
const body = (response?.data ?? {});
|
|
260
241
|
const layout = body.layout;
|
|
242
|
+
const theme = body.project?.theme;
|
|
243
|
+
// undefined: stil raporu hiç yazılmaz. null: tema yok ama kaskad yine çözülür.
|
|
244
|
+
const styleSource = resolveStyles ? (theme ?? null) : undefined;
|
|
261
245
|
const asNodes = (value) => (Array.isArray(value) ? value : []);
|
|
262
246
|
const named = (value) => (Array.isArray(value) ? value : []).map(entry => ({ id: entry.id, name: entry.name, slug: entry.slug }));
|
|
263
247
|
return {
|
|
264
248
|
page: { id: body.id, name: body.name, slug: body.slug, browserTitle: body.browserTitle, isHomepage: body.isHomepage },
|
|
265
249
|
events: body.events ?? [],
|
|
266
|
-
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(summarize) } : null,
|
|
267
|
-
schema: asNodes(body.schema).map(summarize),
|
|
250
|
+
layout: layout ? { id: layout.id, name: layout.name, views: asNodes(layout.views).map(node => summarize(node, styleSource)) } : null,
|
|
251
|
+
schema: asNodes(body.schema).map(node => summarize(node, styleSource)),
|
|
268
252
|
widgets: named(body.widgets),
|
|
269
253
|
flows: named(body.flows),
|
|
270
254
|
variableKeys: Object.keys((body.variables ?? {})),
|
|
271
255
|
hasTheme: Boolean(body.project?.theme),
|
|
272
|
-
note:
|
|
256
|
+
note: resolveStyles
|
|
257
|
+
? "schema, layout'taki slot'un İÇİNE yerleşerek çizilir (Kural 1f). resolvedStyle = componentStyles → preset → düğüm stili kaskadı (aibox'ta tipli grupların derlemesi TABAN katmandır); styleLayers hangi katmanların konuştuğunu söyler."
|
|
258
|
+
: "schema, layout'taki slot'un İÇİNE yerleşerek çizilir (Kural 1f); tam gövde gerekirse view:'full'. Bir düğümün ekranda GERÇEKTEN aldığı stili görmek için resolveStyles:true."
|
|
273
259
|
};
|
|
274
260
|
}));
|
|
261
|
+
const screenshotter = new StudioScreenshotter(config);
|
|
262
|
+
const maxInlinePngBytes = 5 * 1024 * 1024;
|
|
263
|
+
server.registerTool('page_screenshot', {
|
|
264
|
+
title: 'Sayfanın ekran görüntüsünü al',
|
|
265
|
+
description: "Studio arayüzünün runtime render rotasını (/dev ya da /view) headless Chromium'da açar ve sayfanın PNG ekran görüntüsünü inline görüntü olarak döner — kurduğun sayfayı GÖZLE doğrulamanın yolu budur (page_runtime_get paketi verir, bu tool ekranda ne ÇİZİLDİĞİNİ gösterir). Sayfaya bağlı middleware'ler gerçekten koşar; reddedilirse yanıtta middlewareBlocked işaretlenir ve hata/boş durum karesi yine döner — bu da bir doğrulamadır. STUDIO_BASE_URL tanımlı olmalı; ilk kullanım için `npx playwright-core install chromium` gerekir.",
|
|
266
|
+
inputSchema: {
|
|
267
|
+
project: projectParam,
|
|
268
|
+
environment: environmentParam,
|
|
269
|
+
applicationSlug: z.string().min(1).describe("Uygulamanın slug'ı (application_list / project_overview)"),
|
|
270
|
+
pageSlug: z.string().min(1).describe("Sayfanın slug'ı (page_list)"),
|
|
271
|
+
viewport: z.object({
|
|
272
|
+
width: z.number().int().min(320).max(3840),
|
|
273
|
+
height: z.number().int().min(240).max(2160)
|
|
274
|
+
}).optional().describe('Varsayılan 1280x800 (deviceScaleFactor 1)'),
|
|
275
|
+
fullPage: z.boolean().optional().describe('true: kaydırılan kısım dahil tüm sayfa; varsayılan yalnız viewport karesi'),
|
|
276
|
+
delayMs: z.number().int().min(0).max(10000).optional().describe('Animasyon/geç gelen veri için EK bekleme; 600 ms yerleşme beklemesi her zaman uygulanır'),
|
|
277
|
+
anonymous: z.boolean().optional().describe("true: NG_AccessToken çerezi enjekte edilmez — anonim ziyaretçinin gördüğü kare. Korumalı sayfada middlewareBlocked bekleyin."),
|
|
278
|
+
outPath: z.string().min(1).optional().describe('Verilirse PNG bu yerel yola yazılır ve inline görüntü DÖNMEZ; yalnız özet (yol, boyut) döner — büyük fullPage kareleri için')
|
|
279
|
+
},
|
|
280
|
+
annotations: readOnly
|
|
281
|
+
}, async ({ project, environment, applicationSlug, pageSlug, viewport, fullPage, delayMs, anonymous, outPath }) => {
|
|
282
|
+
try {
|
|
283
|
+
if (!config.studioBaseUrl) {
|
|
284
|
+
throw new PlatformApiError("page_screenshot için STUDIO_BASE_URL tanımlı değil. Studio arayüzünün kök adresini .env'e ekleyin (yerel geliştirmede http://localhost:3000, dağıtımda nginx adresi).");
|
|
285
|
+
}
|
|
286
|
+
const resolvedEnvironment = platform.resolveEnvironment(environment);
|
|
287
|
+
const lane = resolvedEnvironment === 'development' ? 'dev' : resolvedEnvironment === 'production' ? 'view' : undefined;
|
|
288
|
+
if (!lane) {
|
|
289
|
+
throw new PlatformApiError(`page_screenshot yalnız 'development' (/dev) ve 'production' (/view) ortamlarını görüntüleyebilir; '${resolvedEnvironment}' için Studio'da karşılık gelen bir rota yok.`);
|
|
290
|
+
}
|
|
291
|
+
const url = `${config.studioBaseUrl}/${lane}/${platform.resolveProject(project)}/${applicationSlug}/${pageSlug}`;
|
|
292
|
+
const size = viewport ?? { width: 1280, height: 800 };
|
|
293
|
+
const shot = await screenshotter.capture({
|
|
294
|
+
url,
|
|
295
|
+
apiPathMarker: `/${applicationSlug}/p/${pageSlug}`,
|
|
296
|
+
token: anonymous ? undefined : await platform.accessToken(),
|
|
297
|
+
cookieUrl: config.studioBaseUrl,
|
|
298
|
+
viewport: size,
|
|
299
|
+
fullPage: fullPage === true,
|
|
300
|
+
delayMs: delayMs ?? 0
|
|
301
|
+
});
|
|
302
|
+
const meta = {
|
|
303
|
+
url,
|
|
304
|
+
environment: resolvedEnvironment,
|
|
305
|
+
viewport: size,
|
|
306
|
+
fullPage: fullPage === true,
|
|
307
|
+
sizeBytes: shot.png.byteLength,
|
|
308
|
+
elapsedMs: shot.elapsedMs,
|
|
309
|
+
...(shot.apiStatus === undefined ? {} : { apiStatus: shot.apiStatus }),
|
|
310
|
+
...(shot.apiStatus !== undefined && shot.apiStatus >= 300
|
|
311
|
+
? { middlewareBlocked: true, note: 'Sayfanın veri isteği reddedildi — karede hata/boş durum görünüyor olabilir. Korumalı sayfada kimliksiz/yetkisiz isteğin reddedilmesi BEKLENEN davranıştır (page_runtime_get ile karşılaştırın).' }
|
|
312
|
+
: {}),
|
|
313
|
+
...(shot.renderWaitTimedOut
|
|
314
|
+
? { renderWaitTimedOut: true, note: "Sayfanın veri isteği gözlenmedi — kare hata/boş durumu gösteriyor olabilir; STUDIO_BASE_URL ve slug'ları doğrulayın." }
|
|
315
|
+
: {})
|
|
316
|
+
};
|
|
317
|
+
if (outPath) {
|
|
318
|
+
const absolute = resolve(outPath);
|
|
319
|
+
try {
|
|
320
|
+
await writeFile(absolute, shot.png);
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
324
|
+
throw new PlatformApiError(`page_screenshot: dosya yazılamadı — ${absolute} (${message}). Yol gateway sürecinin çalıştığı makineye göre çözülür.`);
|
|
325
|
+
}
|
|
326
|
+
return toolResult({ path: absolute, ...meta }, config);
|
|
327
|
+
}
|
|
328
|
+
if (shot.png.byteLength > maxInlinePngBytes) {
|
|
329
|
+
throw new PlatformApiError(`Görüntü ${shot.png.byteLength} B — inline sınırı (${maxInlinePngBytes} B) aşıyor. outPath verin (dosyaya yazılır, model bağlamına girmez) ya da fullPage'i kapatıp viewport'u küçültün.`);
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
content: [
|
|
333
|
+
{ type: 'image', data: shot.png.toString('base64'), mimeType: 'image/png' },
|
|
334
|
+
{ type: 'text', text: JSON.stringify(meta) }
|
|
335
|
+
],
|
|
336
|
+
structuredContent: meta
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
return toolError(error);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
275
343
|
server.registerTool('page_schema_get', {
|
|
276
344
|
title: 'Sayfa şemasını oku',
|
|
277
345
|
description: "Sayfanın komponent şemasını okur; yanıttaki updatedAt değeri page_schema_patch'in expectedUpdatedAt kontrolünde kullanılır.",
|
|
@@ -297,7 +365,7 @@ export function registerPageTools(server, platform) {
|
|
|
297
365
|
throw new PlatformApiError(`Sayfada '${componentId}' id'li bir komponent yok; page_schema_get'i summary kipinde çağırıp hedefi doğrulayın.`);
|
|
298
366
|
return { updatedAt, schema: subtree };
|
|
299
367
|
}
|
|
300
|
-
return { updatedAt, schema: (page.schema ?? []).map(summarize) };
|
|
368
|
+
return { updatedAt, schema: (page.schema ?? []).map(node => summarize(node)) };
|
|
301
369
|
}));
|
|
302
370
|
server.registerTool('page_state', {
|
|
303
371
|
title: 'Sayfa durumunu getir',
|
|
@@ -13,7 +13,7 @@ export function registerPlatformTools(server, platform) {
|
|
|
13
13
|
}, handle(config, () => platform.org('GET', '/connect/userinfo')));
|
|
14
14
|
server.registerTool('platform_guide', {
|
|
15
15
|
title: 'Platform rehberi oku',
|
|
16
|
-
description: "Modülün iş kuralları rehberini döner (workflow, microservice, entity, page, component, html, middleware, view, expressions, getting-started, modify-existing). Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun; sayfa/layout/flow işleri için page, komponent modeli/olay sözleşmeleri için component, hazır komponentler yetmediğinde HTML kaçış kapısı (Mustache, fireEvent, script kuralları) için html, auth/oturum/yetki için middleware, tam metin arama indeksi için view, ifade yazarken kullanılabilir fonksiyon/bağlam sözlüğü (system.*, file.*, Page.*, state.*, Steps…) için expressions rehberi. Örnek gövdeler için ngapps://examples kaynaklarına bakın.",
|
|
16
|
+
description: "Modülün iş kuralları rehberini döner (workflow, microservice, entity, page, layout, component, html, middleware, view, expressions, translation, getting-started, modify-existing). Bir modülde İLK yazma işleminden önce ilgili rehberi okuyun; sayfa/layout/flow işleri için page, yerleşim kabuğunun kendi kuralları (tek slot, views alanı, micro-op yokluğu, mobil yığılma) için layout, komponent modeli/olay sözleşmeleri için component, hazır komponentler yetmediğinde HTML kaçış kapısı (Mustache, fireEvent, script kuralları) için html, auth/oturum/yetki için middleware, tam metin arama indeksi için view, ifade yazarken kullanılabilir fonksiyon/bağlam sözlüğü (system.*, file.*, Page.*, state.*, Steps…) için expressions, sayfa/içerik çevirileri (çoklu dil) için translation rehberi. Örnek gövdeler için ngapps://examples kaynaklarına bakın.",
|
|
17
17
|
inputSchema: {
|
|
18
18
|
guide: z.string().describe('Rehber adı; bilinmeyen ad verilirse mevcut rehberler listelenir')
|
|
19
19
|
},
|
|
@@ -27,7 +27,7 @@ export function registerPlatformTools(server, platform) {
|
|
|
27
27
|
}));
|
|
28
28
|
server.registerTool('platform_gap_report', {
|
|
29
29
|
title: 'Kabiliyet boşluğu bildir',
|
|
30
|
-
description: "Yapmak İSTEDİĞİNİZ ama platform desteklemediği için yapamadığınız her noktada BUNU ÇAĞIRIN — tasarımı sessizce küçültmeyin. Bu çağrı platforma gitmez, yalnızca günlüğe 'KABİLİYET BOŞLUĞU' bloğu yazar; hata günlüğünde görünmeyen bu kayıtlar ürün yol haritasının girdisidir. Önce rehbere bakıp (platform_guide
|
|
30
|
+
description: "Yapmak İSTEDİĞİNİZ ama platform desteklemediği için yapamadığınız her noktada BUNU ÇAĞIRIN — tasarımı sessizce küçültmeyin. Bu çağrı platforma gitmez, yalnızca günlüğe 'KABİLİYET BOŞLUĞU' bloğu yazar; hata günlüğünde görünmeyen bu kayıtlar ürün yol haritasının girdisidir. Önce rehbere bakıp (platform_guide; hazır komponent yetmiyorsa tek seferlik için 'html' kaçış kapısı, kalıcı bir ihtiyaç içinse 'external-component' — kendi komponentinizi yazıp yayınlayabilirsiniz) gerçekten desteklenmediğinden emin olun: desteklenen bir şeyi 'yok' diye bildirmek yanlış yol haritası üretir.",
|
|
31
31
|
inputSchema: {
|
|
32
32
|
goal: z.string().min(1).describe('Ne yapmak istediniz — kullanıcıya görünen sonuç olarak yazın ("randevu listesini hasta adına göre süzmek")'),
|
|
33
33
|
blocker: z.string().min(1).describe('Neden yapamadınız — hangi kabiliyetin eksik olduğu ("uçta X yok", "komponentte Y prop\'u yok")'),
|
package/dist/tools/projects.js
CHANGED
|
@@ -79,12 +79,14 @@ export function registerProjectTools(server, platform) {
|
|
|
79
79
|
}));
|
|
80
80
|
server.registerTool('project_update', {
|
|
81
81
|
title: 'Proje güncelle',
|
|
82
|
-
description: 'Proje adını, slug\'
|
|
82
|
+
description: 'Proje adını, slug\'ını, temasını ve dil ayarlarını günceller. languages/defaultLanguage verilmezse mevcut değerlerine dokunulmaz. LoaderImage bu tool\'da desteklenmez.',
|
|
83
83
|
inputSchema: {
|
|
84
84
|
id: guid,
|
|
85
85
|
name: z.string().min(1),
|
|
86
86
|
slug: z.string().optional(),
|
|
87
|
-
theme: z.string().optional()
|
|
87
|
+
theme: z.string().optional(),
|
|
88
|
+
languages: z.array(z.string().min(1)).min(1).optional().describe('ISO 639-1 kod listesi (proje dilleri); sayfa çevirileri bu kodlarla doğrulanır'),
|
|
89
|
+
defaultLanguage: z.string().min(1).optional().describe("languages içinde olmalı — proje dilleri hiç kurulmadıysa (ilk kurulumda) tek başına verilebilir")
|
|
88
90
|
},
|
|
89
91
|
annotations: updates
|
|
90
92
|
}, handle(config, args => {
|
|
@@ -93,6 +95,9 @@ export function registerProjectTools(server, platform) {
|
|
|
93
95
|
form.set('Slug', args.slug);
|
|
94
96
|
if (args.theme)
|
|
95
97
|
form.set('Theme', args.theme);
|
|
98
|
+
args.languages?.forEach(language => form.append('Languages', language));
|
|
99
|
+
if (args.defaultLanguage)
|
|
100
|
+
form.set('DefaultLanguage', args.defaultLanguage);
|
|
96
101
|
return platform.org('PUT', `/projects/${args.id}`, { form });
|
|
97
102
|
}));
|
|
98
103
|
server.registerTool('project_delete', {
|
package/dist/tools/screens.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PlatformApiError } from '../platform/errors.js';
|
|
2
|
+
import { resolveNodeStyleReport } from '../knowledge/styleResolve.js';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
export const hostParam = z.enum(['page', 'layout', 'component'])
|
|
4
5
|
.describe("Akışın barındırıcısı: page | layout | component (widget de component'tir)");
|
|
@@ -6,6 +7,41 @@ export const schemaParam = z.array(z.record(z.unknown()))
|
|
|
6
7
|
.describe('Komponent ağacı (DesignComponentManagementSchema listesi); verilmezse mevcut şema korunur');
|
|
7
8
|
export const eventsParam = z.array(z.record(z.unknown()))
|
|
8
9
|
.describe("Sayfa/layout düzeyi olaylar: [{ name, value }] — value bir flow id'sidir");
|
|
10
|
+
export const modeParam = z.enum(['summary', 'subtree', 'full']).optional().describe("summary (varsayılan): yalnız id/elementId/name/type ağacı — hedef bulmak için bunu kullanın. subtree: componentId ile tek alt ağaç. full: tüm şema.");
|
|
11
|
+
/**
|
|
12
|
+
* Ağacı yalnız kimlik alanlarına indirger; hedef aramak için okunur boyutta tutar.
|
|
13
|
+
* `styleSource` verilirse (undefined DEĞİLSE) her düğüme efektif stil raporu da
|
|
14
|
+
* yazılır: tema componentStyles → preset → düğümün kendi stili kaskadı.
|
|
15
|
+
*/
|
|
16
|
+
export function summarize(node, styleSource) {
|
|
17
|
+
const summary = {
|
|
18
|
+
id: node.id,
|
|
19
|
+
elementId: node.elementId,
|
|
20
|
+
name: node.name,
|
|
21
|
+
type: node.type
|
|
22
|
+
};
|
|
23
|
+
if (styleSource !== undefined) {
|
|
24
|
+
const report = resolveNodeStyleReport(node, styleSource ?? undefined);
|
|
25
|
+
if (report.resolvedStyle !== undefined)
|
|
26
|
+
summary.resolvedStyle = report.resolvedStyle;
|
|
27
|
+
summary.styleLayers = report.styleLayers;
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
30
|
+
summary.children = node.children.map(child => summarize(child, styleSource));
|
|
31
|
+
}
|
|
32
|
+
return summary;
|
|
33
|
+
}
|
|
34
|
+
/** elementId ya da id ile ağaçta düğüm bulur. */
|
|
35
|
+
export function findNode(nodes, target) {
|
|
36
|
+
for (const node of nodes ?? []) {
|
|
37
|
+
if (node.elementId === target || String(node.id) === target)
|
|
38
|
+
return node;
|
|
39
|
+
const found = findNode(node.children, target);
|
|
40
|
+
if (found)
|
|
41
|
+
return found;
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
9
45
|
function asArray(value) {
|
|
10
46
|
return Array.isArray(value) ? value : [];
|
|
11
47
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, updates } from './common.js';
|
|
2
|
+
import { handle } from '../platform/responses.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
const translationKeyInput = z.object({
|
|
5
|
+
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ı"),
|
|
7
|
+
componentId: guid.optional().describe("Çeviriyi belirli bir komponente bağlar; o komponent silinince kaydı da silinir")
|
|
8
|
+
});
|
|
9
|
+
export function registerTranslationTools(server, platform) {
|
|
10
|
+
const config = platform.config;
|
|
11
|
+
server.registerTool('translation_list', {
|
|
12
|
+
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').",
|
|
14
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
|
|
15
|
+
annotations: readOnly
|
|
16
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, `/translations/${applicationId}`, { environment })));
|
|
17
|
+
server.registerTool('translation_update', {
|
|
18
|
+
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.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
project: projectParam,
|
|
22
|
+
environment: environmentParam,
|
|
23
|
+
applicationId: guid,
|
|
24
|
+
keys: z.array(translationKeyInput).optional().describe('Eklenecek/güncellenecek anahtarlar'),
|
|
25
|
+
deletedKeys: z.array(z.string().min(1)).optional().describe('Silinecek anahtar adları')
|
|
26
|
+
},
|
|
27
|
+
annotations: updates
|
|
28
|
+
}, handle(config, ({ project, environment, applicationId, keys, deletedKeys }) => {
|
|
29
|
+
if (!keys?.length && !deletedKeys?.length) {
|
|
30
|
+
throw new Error("'keys' ya da 'deletedKeys' parametrelerinden en az biri dolu olmalı.");
|
|
31
|
+
}
|
|
32
|
+
return platform.tenant('PUT', project, `/translations/${applicationId}`, {
|
|
33
|
+
environment,
|
|
34
|
+
body: { keys: keys ?? [], deletedKeys: deletedKeys ?? [] }
|
|
35
|
+
});
|
|
36
|
+
}));
|
|
37
|
+
}
|
package/dist/tools/workflows.js
CHANGED
|
@@ -106,7 +106,7 @@ export function registerWorkflowTools(server, platform) {
|
|
|
106
106
|
}));
|
|
107
107
|
server.registerTool('workflow_create', {
|
|
108
108
|
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. Middleware (istek öncesi koşan guard/setter, user değişkeni, alfabetik sıra) için platform_guide('middleware') + ngapps://examples/middleware-authenticate. Gövde politika kontrolünden geçer.",
|
|
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.",
|
|
110
110
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
111
111
|
annotations: writes
|
|
112
112
|
}, handle(config, async ({ project, environment, applicationId, definition, policyOverride }) => {
|
|
@@ -117,7 +117,7 @@ export function registerWorkflowTools(server, platform) {
|
|
|
117
117
|
}));
|
|
118
118
|
server.registerTool('workflow_update', {
|
|
119
119
|
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. Gövde politika kontrolünden geçer.',
|
|
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.',
|
|
121
121
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
|
|
122
122
|
annotations: updates
|
|
123
123
|
}, handle(config, async ({ project, environment, applicationId, id, definition, policyOverride }) => {
|
|
@@ -128,7 +128,7 @@ export function registerWorkflowTools(server, platform) {
|
|
|
128
128
|
}));
|
|
129
129
|
server.registerTool('workflow_patch', {
|
|
130
130
|
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. Yanıt outline özetidir.',
|
|
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
132
|
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid, operations: patchOperationsParam, policyOverride: policyOverrideParam },
|
|
133
133
|
annotations: updates
|
|
134
134
|
}, handle(config, async ({ project, environment, applicationId, id, operations, policyOverride }) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appaflytech/zeyrek-ai-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP gateway exposing the NgApps platform APIs as tools",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
15
|
".env.example",
|
|
16
|
+
".env.production.example",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"publishConfig": {
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"prepack": "npm run build",
|
|
27
28
|
"typecheck": "tsc --noEmit",
|
|
28
29
|
"test": "vitest run",
|
|
30
|
+
"docs:lint": "tsx scripts/docs-lint.ts",
|
|
29
31
|
"generate-schemas": "tsx scripts/generate-schemas.ts",
|
|
30
32
|
"generate-reserved-tables": "tsx scripts/generate-reserved-tables.ts",
|
|
31
33
|
"harvest-examples": "tsx scripts/harvest-examples.ts"
|
|
@@ -36,6 +38,7 @@
|
|
|
36
38
|
"express": "^4.21.0",
|
|
37
39
|
"fast-json-patch": "^3.1.1",
|
|
38
40
|
"pino": "^9.5.0",
|
|
41
|
+
"playwright-core": "^1.49.0",
|
|
39
42
|
"undici": "^7.3.0",
|
|
40
43
|
"zod": "^3.24.1"
|
|
41
44
|
},
|