@appaflytech/zeyrek-ai-mcp 0.0.1

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 (88) hide show
  1. package/.env.example +46 -0
  2. package/README.md +260 -0
  3. package/dist/config.js +128 -0
  4. package/dist/index.js +88 -0
  5. package/dist/knowledge/componentCatalog.js +438 -0
  6. package/dist/knowledge/componentEvents.js +164 -0
  7. package/dist/knowledge/coverage.js +60 -0
  8. package/dist/knowledge/normalize.js +1309 -0
  9. package/dist/knowledge/outlines.js +167 -0
  10. package/dist/knowledge/policy.js +44 -0
  11. package/dist/knowledge/reservedTables.js +75 -0
  12. package/dist/knowledge/rules.js +1725 -0
  13. package/dist/observability/trace.js +337 -0
  14. package/dist/platform/errors.js +132 -0
  15. package/dist/platform/http.js +169 -0
  16. package/dist/platform/responses.js +94 -0
  17. package/dist/platform/token.js +79 -0
  18. package/dist/resources/examples/entity-pair-m2m.json +105 -0
  19. package/dist/resources/examples/flow-list-to-form.json +65 -0
  20. package/dist/resources/examples/flow-load-table.json +178 -0
  21. package/dist/resources/examples/microservice-table-list.json +49 -0
  22. package/dist/resources/examples/middleware-authenticate.json +194 -0
  23. package/dist/resources/examples/page-detail-profile.json +95 -0
  24. package/dist/resources/examples/page-dynamic-form.json +221 -0
  25. package/dist/resources/examples/page-form-upsert.json +279 -0
  26. package/dist/resources/examples/page-html-embed.json +106 -0
  27. package/dist/resources/examples/page-sign-in.json +194 -0
  28. package/dist/resources/examples/workflow-basic.json +94 -0
  29. package/dist/resources/guides/component.md +108 -0
  30. package/dist/resources/guides/entity.md +348 -0
  31. package/dist/resources/guides/expressions.md +139 -0
  32. package/dist/resources/guides/getting-started.md +109 -0
  33. package/dist/resources/guides/html.md +106 -0
  34. package/dist/resources/guides/microservice.md +313 -0
  35. package/dist/resources/guides/middleware.md +271 -0
  36. package/dist/resources/guides/modify-existing.md +35 -0
  37. package/dist/resources/guides/page.md +1574 -0
  38. package/dist/resources/guides/view.md +89 -0
  39. package/dist/resources/guides/workflow.md +359 -0
  40. package/dist/resources/knowledge.js +44 -0
  41. package/dist/resources/schemas/CreateDatasourceCommand.json +54 -0
  42. package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +133 -0
  43. package/dist/resources/schemas/CreateEntityCommand.json +387 -0
  44. package/dist/resources/schemas/CreateMicroserviceCommand.json +528 -0
  45. package/dist/resources/schemas/CreateQueryCommand.json +43 -0
  46. package/dist/resources/schemas/CreateViewCommand.json +158 -0
  47. package/dist/resources/schemas/CreateWorkflowCommand.json +1719 -0
  48. package/dist/resources/schemas/Filter.json +124 -0
  49. package/dist/resources/schemas/GetRowsQuery.json +122 -0
  50. package/dist/resources/schemas/PreviewMicroserviceCommand.json +565 -0
  51. package/dist/resources/schemas/PreviewQueryCommand.json +74 -0
  52. package/dist/resources/schemas/PreviewWorkflowCommand.json +981 -0
  53. package/dist/resources/schemas/StepDto.json +900 -0
  54. package/dist/resources/schemas/UpdateDatasourceCommand.json +58 -0
  55. package/dist/resources/schemas/UpdateEntityCommand.json +679 -0
  56. package/dist/resources/schemas/UpdateMicroserviceCommand.json +512 -0
  57. package/dist/resources/schemas/UpdateQueryCommand.json +86 -0
  58. package/dist/resources/schemas/UpdateViewCommand.json +153 -0
  59. package/dist/resources/schemas/UpdateWorkflowCommand.json +1719 -0
  60. package/dist/resources/schemas.js +40 -0
  61. package/dist/server.js +63 -0
  62. package/dist/tools/access.js +27 -0
  63. package/dist/tools/applications.js +59 -0
  64. package/dist/tools/automations.js +103 -0
  65. package/dist/tools/common.js +18 -0
  66. package/dist/tools/components.js +280 -0
  67. package/dist/tools/datasources.js +82 -0
  68. package/dist/tools/dependencies.js +17 -0
  69. package/dist/tools/deployments.js +17 -0
  70. package/dist/tools/dtos.js +54 -0
  71. package/dist/tools/entities.js +166 -0
  72. package/dist/tools/files.js +138 -0
  73. package/dist/tools/flows.js +226 -0
  74. package/dist/tools/folders.js +51 -0
  75. package/dist/tools/layouts.js +105 -0
  76. package/dist/tools/microservices.js +198 -0
  77. package/dist/tools/organizations.js +65 -0
  78. package/dist/tools/pages.js +597 -0
  79. package/dist/tools/platformInfo.js +44 -0
  80. package/dist/tools/projects.js +152 -0
  81. package/dist/tools/queries.js +48 -0
  82. package/dist/tools/rows.js +115 -0
  83. package/dist/tools/screens.js +97 -0
  84. package/dist/tools/templates.js +65 -0
  85. package/dist/tools/variables.js +94 -0
  86. package/dist/tools/views.js +58 -0
  87. package/dist/tools/workflows.js +196 -0
  88. package/package.json +49 -0
@@ -0,0 +1,40 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { readdirSync, readFileSync } from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+ const schemasDirectory = join(dirname(fileURLToPath(import.meta.url)), 'schemas');
5
+ function listSchemaFiles() {
6
+ try {
7
+ return readdirSync(schemasDirectory).filter(file => file.endsWith('.json'));
8
+ }
9
+ catch {
10
+ return [];
11
+ }
12
+ }
13
+ export function registerSchemaResources(server) {
14
+ const schemaFiles = listSchemaFiles();
15
+ server.registerResource('schema-index', 'ngapps://schemas/index', {
16
+ title: 'Şema dizini',
17
+ description: 'Kullanılabilir API gövde şemalarının listesi; npm run generate-schemas ile güncellenir.',
18
+ mimeType: 'application/json'
19
+ }, async (uri) => ({
20
+ contents: [{
21
+ uri: uri.href,
22
+ mimeType: 'application/json',
23
+ text: JSON.stringify({ schemas: schemaFiles.map(file => `ngapps://schemas/${file.replace(/\.json$/, '')}`) })
24
+ }]
25
+ }));
26
+ for (const file of schemaFiles) {
27
+ const schemaName = file.replace(/\.json$/, '');
28
+ server.registerResource(`schema-${schemaName}`, `ngapps://schemas/${schemaName}`, {
29
+ title: `${schemaName} şeması`,
30
+ description: `${schemaName} API gövdesinin JSON şeması (swagger'dan üretildi)`,
31
+ mimeType: 'application/json'
32
+ }, async (uri) => ({
33
+ contents: [{
34
+ uri: uri.href,
35
+ mimeType: 'application/json',
36
+ text: readFileSync(join(schemasDirectory, file), 'utf8')
37
+ }]
38
+ }));
39
+ }
40
+ }
package/dist/server.js ADDED
@@ -0,0 +1,63 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { registerMicroserviceTools } from './tools/microservices.js';
3
+ import { registerApplicationTools } from './tools/applications.js';
4
+ import { registerDeploymentTools } from './tools/deployments.js';
5
+ import { registerDatasourceTools } from './tools/datasources.js';
6
+ import { registerAutomationTools } from './tools/automations.js';
7
+ import { registerOrganizationTools } from './tools/organizations.js';
8
+ import { registerComponentTools } from './tools/components.js';
9
+ import { registerPlatformTools } from './tools/platformInfo.js';
10
+ import { registerTemplateTools } from './tools/templates.js';
11
+ import { registerWorkflowTools } from './tools/workflows.js';
12
+ import { registerLayoutTools } from './tools/layouts.js';
13
+ import { registerAccessTools } from './tools/access.js';
14
+ import { registerFlowTools } from './tools/flows.js';
15
+ import { registerKnowledgeResources } from './resources/knowledge.js';
16
+ import { registerSchemaResources } from './resources/schemas.js';
17
+ import { instrumentServer } from './observability/trace.js';
18
+ import { registerProjectTools } from './tools/projects.js';
19
+ import { registerEntityTools } from './tools/entities.js';
20
+ import { registerDependencyTools } from './tools/dependencies.js';
21
+ import { registerVariableTools } from './tools/variables.js';
22
+ import { registerFolderTools } from './tools/folders.js';
23
+ import { registerQueryTools } from './tools/queries.js';
24
+ import { registerViewTools } from './tools/views.js';
25
+ import { registerPageTools } from './tools/pages.js';
26
+ import { registerFileTools } from './tools/files.js';
27
+ import { registerRowTools } from './tools/rows.js';
28
+ import { registerDtoTools } from './tools/dtos.js';
29
+ const moduleRegistrations = {
30
+ platform: registerPlatformTools,
31
+ project: registerProjectTools,
32
+ application: registerApplicationTools,
33
+ datasource: registerDatasourceTools,
34
+ entity: registerEntityTools,
35
+ row: registerRowTools,
36
+ microservice: registerMicroserviceTools,
37
+ query: registerQueryTools,
38
+ workflow: registerWorkflowTools,
39
+ automation: registerAutomationTools,
40
+ dto: registerDtoTools,
41
+ view: registerViewTools,
42
+ page: registerPageTools,
43
+ component: registerComponentTools,
44
+ flow: registerFlowTools,
45
+ layout: registerLayoutTools,
46
+ variable: registerVariableTools,
47
+ folder: registerFolderTools,
48
+ file: registerFileTools,
49
+ access: registerAccessTools,
50
+ template: registerTemplateTools,
51
+ organization: registerOrganizationTools,
52
+ dependency: registerDependencyTools,
53
+ deployment: registerDeploymentTools
54
+ };
55
+ export function createGatewayServer(platform) {
56
+ const server = instrumentServer(new McpServer({ name: 'ngapps', version: '0.1.0' }), platform.config);
57
+ for (const moduleName of platform.config.modules) {
58
+ moduleRegistrations[moduleName](server, platform);
59
+ }
60
+ registerSchemaResources(server);
61
+ registerKnowledgeResources(server);
62
+ return server;
63
+ }
@@ -0,0 +1,27 @@
1
+ import { projectParam, environmentParam, pagingParams, readOnly } from './common.js';
2
+ import { handle } from '../platform/responses.js';
3
+ import { z } from 'zod';
4
+ export function registerAccessTools(server, platform) {
5
+ const config = platform.config;
6
+ server.registerTool('role_list', {
7
+ title: 'Rolleri listele',
8
+ description: "Projenin rollerini listeler. page_create/page_update'in roleIds parametresi bu id'lerdir — rol id'sini tahmin etmeyin, buradan okuyun.",
9
+ inputSchema: { project: projectParam, environment: environmentParam },
10
+ annotations: readOnly
11
+ }, handle(config, async ({ project, environment }) => {
12
+ const payload = await platform.tenant('GET', project, '/roles', { environment });
13
+ return { roles: (payload?.data ?? []).map(role => ({ id: role.id, name: role.name, isSuperAdmin: role.isSuperAdmin })) };
14
+ }));
15
+ server.registerTool('user_list', {
16
+ title: 'Kullanıcıları listele',
17
+ description: 'Projenin kullanıcılarını rolleriyle listeler (test kullanıcısı seçmek, satır sahipliği doğrulamak için).',
18
+ inputSchema: { project: projectParam, environment: environmentParam, searchTerm: z.string().optional(), ...pagingParams },
19
+ annotations: readOnly
20
+ }, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/users', { environment, query })));
21
+ server.registerTool('org_role_list', {
22
+ title: 'Organizasyon rollerini listele',
23
+ description: 'Organization.API tarafındaki rolleri listeler (platform yönetimi); proje içi yetkilendirme için role_list kullanın.',
24
+ inputSchema: {},
25
+ annotations: readOnly
26
+ }, handle(config, () => platform.org('GET', '/roles')));
27
+ }
@@ -0,0 +1,59 @@
1
+ import { guid, pagingParams, readOnly, writes, updates, destructive } from './common.js';
2
+ import { handle } from '../platform/responses.js';
3
+ import { z } from 'zod';
4
+ export function registerApplicationTools(server, platform) {
5
+ const config = platform.config;
6
+ server.registerTool('application_list', {
7
+ title: 'Application listele',
8
+ description: 'Organizasyondaki application\'ları listeler; projectId ile daraltın.',
9
+ inputSchema: {
10
+ projectId: guid.optional(),
11
+ searchTerm: z.string().optional(),
12
+ type: z.string().optional().describe('ApplicationType filtresi'),
13
+ ...pagingParams
14
+ },
15
+ annotations: readOnly
16
+ }, handle(config, args => platform.org('GET', '/applications', { query: args })));
17
+ server.registerTool('application_get', {
18
+ title: 'Application getir',
19
+ description: 'Tek application\'ı id ile getirir.',
20
+ inputSchema: { id: guid },
21
+ annotations: readOnly
22
+ }, handle(config, args => platform.org('GET', `/applications/${args.id}`)));
23
+ server.registerTool('application_create', {
24
+ title: 'Application oluştur',
25
+ description: 'Projeye yeni application ekler.',
26
+ inputSchema: {
27
+ projectId: guid,
28
+ slug: z.string().min(1),
29
+ name: z.string().min(1),
30
+ color: z.string().optional(),
31
+ dns: z.string().optional(),
32
+ type: z.string().optional().describe('ApplicationType; varsayılan Web'),
33
+ properties: z.record(z.unknown()).optional(),
34
+ notFoundPageId: guid.optional()
35
+ },
36
+ annotations: writes
37
+ }, handle(config, ({ projectId, ...body }) => platform.org('POST', `/applications/${projectId}`, { body })));
38
+ server.registerTool('application_update', {
39
+ title: 'Application güncelle',
40
+ description: 'Application bilgilerini günceller; homepageId zorunludur (mevcut değeri application_get ile alın).',
41
+ inputSchema: {
42
+ id: guid,
43
+ slug: z.string().min(1),
44
+ name: z.string().min(1),
45
+ homepageId: guid,
46
+ color: z.string().optional(),
47
+ dns: z.string().optional(),
48
+ properties: z.record(z.unknown()).optional(),
49
+ notFoundPageId: guid.optional()
50
+ },
51
+ annotations: updates
52
+ }, handle(config, ({ id, ...body }) => platform.org('PUT', `/applications/${id}`, { body })));
53
+ server.registerTool('application_delete', {
54
+ title: 'Application sil',
55
+ description: 'Application\'ı kalıcı olarak siler.',
56
+ inputSchema: { id: guid },
57
+ annotations: destructive
58
+ }, handle(config, args => platform.org('DELETE', `/applications/${args.id}`)));
59
+ }
@@ -0,0 +1,103 @@
1
+ import { guid, projectParam, environmentParam, definitionParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
2
+ import { normalizeWorkflowDefinition, withNormalization } from '../knowledge/normalize.js';
3
+ import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/policy.js';
4
+ import { policyRules } from '../knowledge/rules.js';
5
+ import { handle } from '../platform/responses.js';
6
+ import { z } from 'zod';
7
+ const policyOverrideParam = z.boolean().optional()
8
+ .describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
9
+ export function registerAutomationTools(server, platform) {
10
+ const config = platform.config;
11
+ server.registerTool('automation_list', {
12
+ title: 'Automation listele',
13
+ description: 'Entity\'ye bağlı automation\'ları listeler.',
14
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, searchTerm: z.string().optional(), ...pagingParams },
15
+ annotations: readOnly
16
+ }, handle(config, ({ project, environment, entityId, ...query }) => platform.tenant('GET', project, `/entities/${entityId}/automation/list`, { environment, query })));
17
+ server.registerTool('automation_get', {
18
+ title: 'Automation getir',
19
+ description: 'Tek automation\'ı step ağacıyla getirir.',
20
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, id: guid },
21
+ annotations: readOnly
22
+ }, handle(config, ({ project, environment, entityId, id }) => platform.tenant('GET', project, `/entities/${entityId}/automation/${id}`, { environment })));
23
+ server.registerTool('automation_create', {
24
+ title: 'Automation oluştur',
25
+ description: "Entity'ye automation bağlar. definition = CreateWorkflowCommand gövdesi ('automation' nesnesi içermeli; entityId route'tan set edilir). İlk kullanımdan önce platform_guide('workflow') okuyun; gövde politika kontrolünden geçer.",
26
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
27
+ annotations: writes
28
+ }, handle(config, async ({ project, environment, entityId, definition, policyOverride }) => {
29
+ const normalized = normalizeWorkflowDefinition(definition);
30
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'create', workflowKind: 'automation' }, policyOverride));
31
+ const payload = await platform.tenant('POST', project, `/entities/${entityId}/automation`, { environment, body: definition });
32
+ return attachFindings(withNormalization(payload, normalized), findings);
33
+ }));
34
+ server.registerTool('automation_update', {
35
+ title: 'Automation güncelle',
36
+ description: "Automation tanımını günceller. definition = UpdateWorkflowCommand gövdesi ('automation' nesnesi içermeli); gövde politika kontrolünden geçer.",
37
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, id: guid, definition: definitionParam, policyOverride: policyOverrideParam },
38
+ annotations: updates
39
+ }, handle(config, async ({ project, environment, entityId, id, definition, policyOverride }) => {
40
+ const normalized = normalizeWorkflowDefinition(definition);
41
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'update', workflowKind: 'automation' }, policyOverride));
42
+ const payload = await platform.tenant('PUT', project, `/entities/${entityId}/automation/${id}`, { environment, body: definition });
43
+ return attachFindings(withNormalization(payload, normalized), findings);
44
+ }));
45
+ server.registerTool('automation_delete', {
46
+ title: 'Automation sil',
47
+ description: 'Automation\'ı siler.',
48
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, id: guid },
49
+ annotations: destructive
50
+ }, handle(config, ({ project, environment, entityId, id }) => platform.tenant('DELETE', project, `/entities/${entityId}/automation/${id}`, { environment })));
51
+ server.registerTool('automation_reorder', {
52
+ title: 'Automation sırasını değiştir',
53
+ description: "Entity'nin automation'larının çalışma sırasını toplu günceller; aynı operasyona bağlı birden çok automation'da sıra davranışı belirler.",
54
+ inputSchema: {
55
+ project: projectParam,
56
+ environment: environmentParam,
57
+ entityId: guid,
58
+ automations: z.array(z.object({ id: guid, order: z.number().int().min(0) })).min(1)
59
+ .describe('Tüm automation\'lar için (id, order) çiftleri; listeyi automation_list ile kurun')
60
+ },
61
+ annotations: updates
62
+ }, handle(config, ({ project, environment, entityId, automations }) => platform.tenant('PUT', project, `/entities/${entityId}/automation/orders`, { environment, body: { automations } })));
63
+ server.registerTool('automation_trigger', {
64
+ title: 'Automation tetikle',
65
+ description: 'Automation\'ı elle tetikler. Fire-and-forget çalışır: sonuç bu yanıtta dönmez, automation_histories ile izleyin.',
66
+ inputSchema: {
67
+ project: projectParam,
68
+ environment: environmentParam,
69
+ entityId: guid,
70
+ id: guid,
71
+ operation: z.string().optional(),
72
+ recordIds: z.array(z.union([z.string(), z.number()])).optional().describe('Tetiklenecek satır id\'leri')
73
+ },
74
+ annotations: writes
75
+ }, handle(config, ({ project, environment, entityId, id, operation, recordIds }) => platform.tenant('POST', project, `/entities/${entityId}/automation/${id}/trigger`, {
76
+ environment,
77
+ body: { operation, recordIds: recordIds ?? [] }
78
+ })));
79
+ server.registerTool('automation_histories', {
80
+ title: 'Automation geçmişi',
81
+ description: 'Automation çalıştırma geçmişini listeler; automation_trigger sonrası sonucu buradan doğrulayın.',
82
+ inputSchema: {
83
+ project: projectParam,
84
+ environment: environmentParam,
85
+ entityId: guid,
86
+ id: guid,
87
+ status: z.string().optional().describe('EventStatus filtresi'),
88
+ ...pagingParams
89
+ },
90
+ annotations: readOnly
91
+ }, handle(config, ({ project, environment, entityId, id, ...query }) => platform.tenant('GET', project, `/entities/${entityId}/automation/${id}/histories`, { environment, query })));
92
+ server.registerTool('automation_preview', {
93
+ title: 'Automation önizle',
94
+ description: 'Kaydedilmemiş automation tanımını çalıştırır; kaydetmeden önce preview ile doğrulayın. definition = PreviewWorkflowCommand gövdesi.',
95
+ inputSchema: { project: projectParam, environment: environmentParam, entityId: guid, definition: definitionParam, policyOverride: policyOverrideParam },
96
+ annotations: writes
97
+ }, handle(config, async ({ project, environment, entityId, definition, policyOverride }) => {
98
+ const normalized = normalizeWorkflowDefinition(definition);
99
+ const findings = enforcePolicy(evaluatePolicy(config, policyRules, 'workflow', definition, { operation: 'preview', workflowKind: 'automation' }, policyOverride));
100
+ const payload = await platform.tenant('POST', project, `/entities/${entityId}/automation/preview`, { environment, body: definition });
101
+ return attachFindings(withNormalization(payload, normalized), findings);
102
+ }));
103
+ }
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ export const guid = z.string().uuid();
3
+ export const projectParam = z.string().min(1).optional()
4
+ .describe('Proje slug\'ı; boşsa DEFAULT_PROJECT kullanılır');
5
+ export const environmentParam = z.string().min(1).optional()
6
+ .describe('Environment adı (X-Environment); boşsa DEFAULT_ENVIRONMENT kullanılır');
7
+ export const definitionParam = z.record(z.unknown())
8
+ .describe('API komut gövdesi birebir geçirilir; şablon için ilgili *_get çıktısını ya da ngapps://schemas kaynağını kullanın');
9
+ export const pagingParams = {
10
+ pageIndex: z.number().int().min(0).optional().describe('0 tabanlı sayfa numarası'),
11
+ pageLength: z.number().int().min(1).max(200).optional().describe('Sayfa boyu; platform varsayılanı 10'),
12
+ order: z.string().optional().describe('Sıralanacak alan adı'),
13
+ isDescending: z.boolean().optional()
14
+ };
15
+ export const readOnly = { readOnlyHint: true, openWorldHint: false };
16
+ export const writes = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
17
+ export const updates = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
18
+ export const destructive = { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false };
@@ -0,0 +1,280 @@
1
+ import { guid, projectParam, environmentParam, readOnly, writes, updates, destructive } from './common.js';
2
+ import { normalizeComponentDefaults, componentDefaultsNote, clampBlockSizes, blockSizeNote, normalizeHeightTypes, heightTypeNote, withNormalization } from '../knowledge/normalize.js';
3
+ import { eventContractsFor, eventPayloadNote } from '../knowledge/componentEvents.js';
4
+ import { knowledgeFor, knowledgeNote } from '../knowledge/componentCatalog.js';
5
+ import { componentUpdateBody, fetchHost, schemaParam, unwrap } from './screens.js';
6
+ import { PlatformApiError } from '../platform/errors.js';
7
+ import { handle } from '../platform/responses.js';
8
+ import { z } from 'zod';
9
+ const typeParam = z.enum(['web', 'mobile']).optional().describe('Hedef platform; varsayılan web');
10
+ const kindParam = z.enum(['component', 'widget']).optional()
11
+ .describe("component: sayfaya konan tekil eleman. widget: kendi şeması olan kapsayıcı (container/modal/drawer)");
12
+ const widgetTypeParam = z.enum(['container', 'modal', 'drawer']).optional()
13
+ .describe("kind:'widget' iken zorunlu");
14
+ async function listComponents(platform, project, environment, applicationId, scope) {
15
+ const path = scope === 'all' ? `/components/${applicationId}/all` : `/components/${applicationId}`;
16
+ const response = await platform.tenant('GET', project, path, {
17
+ environment,
18
+ query: scope === 'all' ? undefined : { pageIndex: 0, pageLength: 999 }
19
+ });
20
+ return response?.data ?? [];
21
+ }
22
+ /**
23
+ * Yerleşim normalizasyonlarının ikisi de aynı katalog listesinden beslenir; tek okumada
24
+ * çıkarılır ki yazma yolları fazladan istek atmasın. Uygulamanın kendi sözlüğü okunur
25
+ * (platformun doğrulaması da onu çözer); `scope:'all'` aynı slug'ı birden fazla kayıtla
26
+ * döndürdüğü için burada kullanılmaz.
27
+ *
28
+ * - `maxSizes`: aynı slug birden fazla geçerse en geniş sınır alınır — fazla kırpmak, geçerli
29
+ * bir gövdeyi bozmaktan kötüdür.
30
+ * - `autoHeights`: yalnız boolean dönen kayıtlar sözlüğe girer; bilinmeyen tip
31
+ * `normalizeHeightTypes` tarafında auto sayılır (platform varsayılanı odur).
32
+ */
33
+ export async function fetchComponentLayoutRules(platform, project, environment, applicationId) {
34
+ return componentLayoutRules(await listComponents(platform, project, environment, applicationId));
35
+ }
36
+ /** Katalog listesini zaten okumuş çağıranlar (component_update) ikinci istek atmasın diye ayrı. */
37
+ export function componentLayoutRules(components) {
38
+ const maxSizes = new Map();
39
+ const autoHeights = new Map();
40
+ for (const component of components) {
41
+ const slug = typeof component.slug === 'string' ? component.slug : undefined;
42
+ if (!slug)
43
+ continue;
44
+ if (typeof component.isDefaultAutoHeight === 'boolean')
45
+ autoHeights.set(slug, component.isDefaultAutoHeight);
46
+ const blocks = (component.definition ?? {}).blocks;
47
+ if (!Array.isArray(blocks))
48
+ continue;
49
+ for (const block of blocks) {
50
+ const maxSize = block?.maxSize;
51
+ if (!maxSize)
52
+ continue;
53
+ const current = maxSizes.get(slug);
54
+ maxSizes.set(slug, {
55
+ w: Math.max(current?.w ?? 0, typeof maxSize.w === 'number' ? maxSize.w : 0) || undefined,
56
+ h: Math.max(current?.h ?? 0, typeof maxSize.h === 'number' ? maxSize.h : 0) || undefined
57
+ });
58
+ }
59
+ }
60
+ return { maxSizes, autoHeights };
61
+ }
62
+ /**
63
+ * Katalog okuması başarısız olsa da yazma isteği gitmeli, ama iki normalizasyonun düşüş davranışı
64
+ * AYNI DEĞİLDİR:
65
+ *
66
+ * - Kırpma atlanır — sınır bilgisi yoksa platformun kendi doğrulaması devreye girer.
67
+ * - `heightType` yine yazılır (bilinmeyen tip = auto) — burada atlamak, platformun yanlış
68
+ * varsayılanını (`fixed`, `DesignComponentManagementSchema.cs:24`) kabul etmek demektir; en fazla
69
+ * sekiz tipte (auth-layout/html/image/layout/qrcode/table/text-editor/video) fazladan yükseklik
70
+ * üretir, atlamak ise her tipte içerik kırpar.
71
+ */
72
+ export async function applyCatalogLayoutRules(platform, project, environment, applicationId, nodes) {
73
+ try {
74
+ const rules = await fetchComponentLayoutRules(platform, project, environment, applicationId);
75
+ return { clamped: clampBlockSizes(nodes, rules.maxSizes), heights: normalizeHeightTypes(nodes, rules.autoHeights) };
76
+ }
77
+ catch {
78
+ return { clamped: [], heights: normalizeHeightTypes(nodes, new Map()) };
79
+ }
80
+ }
81
+ /**
82
+ * slug → komponentin YAYDIĞI olay adları. `set_event`'in hedef doğrulaması bunu kullanır: platform
83
+ * uydurma bir olay adını 200 ile kabul eder ve bağ sessizce ölü kalır (destek masası koşumu B-03:
84
+ * rehberdeki `onClick` gerçekte `click`). Olay listesi boş dönen kayıtlar sözlüğe girmez — bilgi
85
+ * yoksa doğrulama da yapılmaz.
86
+ */
87
+ export async function fetchComponentEventNames(platform, project, environment, applicationId) {
88
+ const events = new Map();
89
+ for (const component of await listComponents(platform, project, environment, applicationId)) {
90
+ const slug = typeof component.slug === 'string' ? component.slug : undefined;
91
+ const declared = (component.definition ?? {}).events;
92
+ if (!slug || !Array.isArray(declared) || declared.length === 0)
93
+ continue;
94
+ const names = declared
95
+ .map(event => String(event?.name ?? ''))
96
+ .filter(name => name.length > 0);
97
+ if (names.length > 0)
98
+ events.set(slug, [...new Set([...(events.get(slug) ?? []), ...names])]);
99
+ }
100
+ return events;
101
+ }
102
+ export function registerComponentTools(server, platform) {
103
+ const config = platform.config;
104
+ server.registerTool('component_list', {
105
+ title: 'Komponent sözlüğünü listele',
106
+ description: "Sayfaya konabilecek kayıtlı komponent tiplerini (slug, name, kategori, droppable) kompakt listeler; tek tipin tam sözlüğü + davranış bilgisi için component_get, komponent modelinin geneli için platform_guide('component'). scope:'all' platformun varsayılan kataloğunu da getirir.",
107
+ inputSchema: {
108
+ project: projectParam,
109
+ environment: environmentParam,
110
+ applicationId: guid,
111
+ scope: z.enum(['application', 'all']).optional().describe("application (varsayılan): bu uygulamanın sözlüğü. all: platform genelindeki tüm kayıtlı komponentler"),
112
+ includeHidden: z.boolean().optional().describe('isHide işaretli komponentleri de getirir')
113
+ },
114
+ annotations: readOnly
115
+ }, handle(config, async ({ project, environment, applicationId, scope, includeHidden }) => {
116
+ const components = await listComponents(platform, project, environment, applicationId, scope);
117
+ return {
118
+ components: components
119
+ .filter(component => includeHidden === true || component.isHide !== true)
120
+ .map(component => ({
121
+ id: component.id,
122
+ slug: component.slug,
123
+ name: component.name,
124
+ description: component.description,
125
+ categories: component.categories,
126
+ isDroppable: component.isDroppable,
127
+ kind: component.kind,
128
+ widgetType: component.widgetType
129
+ }))
130
+ };
131
+ }));
132
+ server.registerTool('component_get', {
133
+ title: 'Komponent sözlüğünü getir',
134
+ description: "Tek komponent tipinin tam sözlüğü: prop'lar (tip/enum/default), event'ler (yükleriyle), davranış bilgisi (knowledge) ve boyut sınırları. Şemaya uydurma prop yazmayın — yalnız burada dönenleri kullanın. Yanıtta guide alanı dönerse derin rehberi platform_guide ile okuyun (örn. html). slug ya da id ile çağrılır.",
135
+ inputSchema: {
136
+ project: projectParam,
137
+ environment: environmentParam,
138
+ applicationId: guid,
139
+ slug: z.string().min(1).optional().describe("Komponent tip slug'ı, örn. 'button' ya da 'dropdown'"),
140
+ id: guid.optional().describe('slug verilmediyse zorunlu')
141
+ },
142
+ annotations: readOnly
143
+ }, handle(config, async ({ project, environment, applicationId, slug, id }) => {
144
+ if (!slug && !id)
145
+ throw new PlatformApiError("'slug' ya da 'id' parametrelerinden biri zorunlu.");
146
+ let component;
147
+ if (id) {
148
+ component = unwrap(await platform.tenant('GET', project, `/components/${applicationId}/${id}`, { environment }));
149
+ }
150
+ else {
151
+ const components = await listComponents(platform, project, environment, applicationId);
152
+ component = components.find(entry => entry.slug === slug);
153
+ if (!component) {
154
+ throw new PlatformApiError(`'${slug}' diye kayıtlı bir komponent yok. Kayıtlı slug'lar: ${components.map(entry => entry.slug).sort().join(', ')}`);
155
+ }
156
+ }
157
+ const definition = (component.definition ?? {});
158
+ const eventNames = Array.isArray(definition.events)
159
+ ? definition.events.map(event => String(event.name ?? ''))
160
+ : [];
161
+ const componentSlug = typeof component.slug === 'string' ? component.slug : undefined;
162
+ const knowledge = knowledgeFor(componentSlug);
163
+ return {
164
+ id: component.id,
165
+ slug: component.slug,
166
+ name: component.name,
167
+ description: component.description,
168
+ kind: component.kind,
169
+ widgetType: component.widgetType,
170
+ isDroppable: component.isDroppable,
171
+ props: definition.props,
172
+ defaults: definition.defaults,
173
+ events: eventContractsFor(componentSlug, eventNames),
174
+ eventPayloadNote,
175
+ ...(knowledge
176
+ ? {
177
+ knowledge: {
178
+ summary: knowledge.summary,
179
+ ...(knowledge.behavior ? { behavior: knowledge.behavior } : {}),
180
+ ...(knowledge.propNotes ? { propNotes: knowledge.propNotes } : {})
181
+ },
182
+ ...(knowledge.guide ? { guide: knowledge.guide } : {})
183
+ }
184
+ : { knowledgeNote }),
185
+ validations: definition.validations,
186
+ sizes: definition.blocks
187
+ };
188
+ }));
189
+ server.registerTool('component_create', {
190
+ title: 'Komponent/widget oluştur',
191
+ description: "Uygulamaya yeni komponent ya da widget (container/modal/drawer) kaydeder. definition = ComponentDefinitionManagementSchema (props/defaults/events/blocks); mevcut bir kaydı component_get ile alıp şablon olarak kullanın.",
192
+ inputSchema: {
193
+ project: projectParam,
194
+ environment: environmentParam,
195
+ applicationId: guid,
196
+ name: z.string().min(1),
197
+ slug: z.string().min(1),
198
+ kind: kindParam,
199
+ type: typeParam,
200
+ widgetType: widgetTypeParam,
201
+ description: z.string().optional(),
202
+ icon: z.string().optional(),
203
+ categories: z.array(z.string()).optional(),
204
+ definition: z.record(z.unknown()).optional(),
205
+ schema: schemaParam.optional().describe("widget'ın kendi komponent ağacı"),
206
+ isHide: z.boolean().optional(),
207
+ isDroppable: z.boolean().optional(),
208
+ isSticky: z.boolean().optional(),
209
+ isDeletable: z.boolean().optional(),
210
+ isDefaultAutoHeight: z.boolean().optional()
211
+ },
212
+ annotations: writes
213
+ }, handle(config, async ({ project, environment, applicationId, ...body }) => {
214
+ if (body.kind === 'widget' && !body.widgetType) {
215
+ throw new PlatformApiError("kind:'widget' için widgetType (container | modal | drawer) zorunludur.");
216
+ }
217
+ const fixed = normalizeComponentDefaults(body.schema);
218
+ const catalog = (body.schema ?? []).length > 0
219
+ ? await applyCatalogLayoutRules(platform, project, environment, applicationId, body.schema)
220
+ : { clamped: [], heights: [] };
221
+ return platform.tenant('POST', project, `/components/${applicationId}`, {
222
+ environment,
223
+ body: {
224
+ kind: 'component',
225
+ type: 'web',
226
+ categories: [],
227
+ definition: {},
228
+ schema: [],
229
+ ...Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined))
230
+ }
231
+ }).then(response => withNormalization(response, [...componentDefaultsNote(fixed), ...blockSizeNote(catalog.clamped), ...heightTypeNote(catalog.heights)]));
232
+ }));
233
+ server.registerTool('component_update', {
234
+ title: 'Komponent güncelle',
235
+ description: "Komponenti SLUG ile günceller (platformdaki tek slug'la çalışan update ucu; silme id ile). PUT replace olduğu için gateway kaydı önce okur, yalnız verdiğiniz alanları değiştirip tam gövdeyi geri yazar.",
236
+ inputSchema: {
237
+ project: projectParam,
238
+ environment: environmentParam,
239
+ applicationId: guid,
240
+ slug: z.string().min(1).describe("Güncellenecek komponentin slug'ı (id değil)"),
241
+ name: z.string().min(1).optional(),
242
+ type: typeParam,
243
+ widgetType: widgetTypeParam,
244
+ description: z.string().optional(),
245
+ icon: z.string().optional(),
246
+ categories: z.array(z.string()).optional(),
247
+ definition: z.record(z.unknown()).optional(),
248
+ schema: schemaParam.optional(),
249
+ isHide: z.boolean().optional(),
250
+ isDroppable: z.boolean().optional(),
251
+ isSticky: z.boolean().optional(),
252
+ isDeletable: z.boolean().optional(),
253
+ isDefaultAutoHeight: z.boolean().optional()
254
+ },
255
+ annotations: updates
256
+ }, handle(config, async ({ project, environment, applicationId, slug, ...changes }) => {
257
+ const components = await listComponents(platform, project, environment, applicationId);
258
+ const current = components.find(entry => entry.slug === slug);
259
+ if (!current) {
260
+ throw new PlatformApiError(`'${slug}' slug'lı komponent bu uygulamada yok. Kayıtlı slug'lar: ${components.map(entry => entry.slug).sort().join(', ')}`);
261
+ }
262
+ const detailed = current.id
263
+ ? await fetchHost(platform, 'component', project, environment, applicationId, String(current.id))
264
+ : current;
265
+ const overrides = Object.fromEntries(Object.entries(changes).filter(([, value]) => value !== undefined));
266
+ const fixed = normalizeComponentDefaults(overrides.schema);
267
+ const rules = componentLayoutRules(components);
268
+ const clamped = overrides.schema === undefined ? [] : clampBlockSizes(overrides.schema, rules.maxSizes);
269
+ const heights = overrides.schema === undefined ? [] : normalizeHeightTypes(overrides.schema, rules.autoHeights);
270
+ const body = componentUpdateBody({ ...detailed, applicationId }, overrides);
271
+ await platform.tenant('PUT', project, `/components/${applicationId}/${slug}`, { environment, body });
272
+ return withNormalization({ slug, changed: Object.keys(overrides), preserved: { schemaNodes: body.schema.length } }, [...componentDefaultsNote(fixed), ...blockSizeNote(clamped), ...heightTypeNote(heights)]);
273
+ }));
274
+ server.registerTool('component_delete', {
275
+ title: 'Komponent sil',
276
+ description: "Komponenti ID ile siler (update slug alır, delete id alır). Sayfalarda kullanılıyorsa önce dependency_graph module:'component' ile kontrol edin.",
277
+ inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, id: guid },
278
+ annotations: destructive
279
+ }, handle(config, ({ project, environment, applicationId, id }) => platform.tenant('DELETE', project, `/components/${applicationId}/${id}`, { environment })));
280
+ }