@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.
- package/.env.example +46 -0
- package/README.md +260 -0
- package/dist/config.js +128 -0
- package/dist/index.js +88 -0
- package/dist/knowledge/componentCatalog.js +438 -0
- package/dist/knowledge/componentEvents.js +164 -0
- package/dist/knowledge/coverage.js +60 -0
- package/dist/knowledge/normalize.js +1309 -0
- package/dist/knowledge/outlines.js +167 -0
- package/dist/knowledge/policy.js +44 -0
- package/dist/knowledge/reservedTables.js +75 -0
- package/dist/knowledge/rules.js +1725 -0
- package/dist/observability/trace.js +337 -0
- package/dist/platform/errors.js +132 -0
- package/dist/platform/http.js +169 -0
- package/dist/platform/responses.js +94 -0
- package/dist/platform/token.js +79 -0
- package/dist/resources/examples/entity-pair-m2m.json +105 -0
- package/dist/resources/examples/flow-list-to-form.json +65 -0
- package/dist/resources/examples/flow-load-table.json +178 -0
- package/dist/resources/examples/microservice-table-list.json +49 -0
- package/dist/resources/examples/middleware-authenticate.json +194 -0
- package/dist/resources/examples/page-detail-profile.json +95 -0
- package/dist/resources/examples/page-dynamic-form.json +221 -0
- package/dist/resources/examples/page-form-upsert.json +279 -0
- package/dist/resources/examples/page-html-embed.json +106 -0
- package/dist/resources/examples/page-sign-in.json +194 -0
- package/dist/resources/examples/workflow-basic.json +94 -0
- package/dist/resources/guides/component.md +108 -0
- package/dist/resources/guides/entity.md +348 -0
- package/dist/resources/guides/expressions.md +139 -0
- package/dist/resources/guides/getting-started.md +109 -0
- package/dist/resources/guides/html.md +106 -0
- package/dist/resources/guides/microservice.md +313 -0
- package/dist/resources/guides/middleware.md +271 -0
- package/dist/resources/guides/modify-existing.md +35 -0
- package/dist/resources/guides/page.md +1574 -0
- package/dist/resources/guides/view.md +89 -0
- package/dist/resources/guides/workflow.md +359 -0
- package/dist/resources/knowledge.js +44 -0
- package/dist/resources/schemas/CreateDatasourceCommand.json +54 -0
- package/dist/resources/schemas/CreateDiscoveredEntitiesCommand.json +133 -0
- package/dist/resources/schemas/CreateEntityCommand.json +387 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +528 -0
- package/dist/resources/schemas/CreateQueryCommand.json +43 -0
- package/dist/resources/schemas/CreateViewCommand.json +158 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +1719 -0
- package/dist/resources/schemas/Filter.json +124 -0
- package/dist/resources/schemas/GetRowsQuery.json +122 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +565 -0
- package/dist/resources/schemas/PreviewQueryCommand.json +74 -0
- package/dist/resources/schemas/PreviewWorkflowCommand.json +981 -0
- package/dist/resources/schemas/StepDto.json +900 -0
- package/dist/resources/schemas/UpdateDatasourceCommand.json +58 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +679 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +512 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +86 -0
- package/dist/resources/schemas/UpdateViewCommand.json +153 -0
- package/dist/resources/schemas/UpdateWorkflowCommand.json +1719 -0
- package/dist/resources/schemas.js +40 -0
- package/dist/server.js +63 -0
- package/dist/tools/access.js +27 -0
- package/dist/tools/applications.js +59 -0
- package/dist/tools/automations.js +103 -0
- package/dist/tools/common.js +18 -0
- package/dist/tools/components.js +280 -0
- package/dist/tools/datasources.js +82 -0
- package/dist/tools/dependencies.js +17 -0
- package/dist/tools/deployments.js +17 -0
- package/dist/tools/dtos.js +54 -0
- package/dist/tools/entities.js +166 -0
- package/dist/tools/files.js +138 -0
- package/dist/tools/flows.js +226 -0
- package/dist/tools/folders.js +51 -0
- package/dist/tools/layouts.js +105 -0
- package/dist/tools/microservices.js +198 -0
- package/dist/tools/organizations.js +65 -0
- package/dist/tools/pages.js +597 -0
- package/dist/tools/platformInfo.js +44 -0
- package/dist/tools/projects.js +152 -0
- package/dist/tools/queries.js +48 -0
- package/dist/tools/rows.js +115 -0
- package/dist/tools/screens.js +97 -0
- package/dist/tools/templates.js +65 -0
- package/dist/tools/variables.js +94 -0
- package/dist/tools/views.js +58 -0
- package/dist/tools/workflows.js +196 -0
- 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
|
+
}
|