@appaflytech/zeyrek-ai-mcp 0.0.35 → 0.0.37
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 +15 -0
- package/README.md +5 -5
- package/dist/config.js +37 -0
- package/dist/index.js +2 -0
- package/dist/knowledge/access.js +802 -0
- package/dist/knowledge/backendConstraints.js +18 -1
- package/dist/knowledge/case.js +41 -0
- package/dist/knowledge/componentCatalog.js +655 -424
- package/dist/knowledge/componentEvents.js +140 -23
- package/dist/knowledge/connectDataBind.js +843 -0
- package/dist/knowledge/connectDataDefinition.js +344 -0
- package/dist/knowledge/connectDataExecute.js +93 -0
- package/dist/knowledge/connectDataRules.js +128 -0
- package/dist/knowledge/decision.js +121 -0
- package/dist/knowledge/expressionPrelude.js +59 -0
- package/dist/knowledge/layoutSummary.js +140 -2
- package/dist/knowledge/microserviceParameters.js +58 -0
- package/dist/knowledge/mining.js +94 -0
- package/dist/knowledge/mobileComponentCatalog.js +864 -0
- package/dist/knowledge/mobileComponentEvents.js +239 -0
- package/dist/knowledge/mobileStore.js +321 -0
- package/dist/knowledge/normalize.js +235 -84
- package/dist/knowledge/outlines.js +36 -0
- package/dist/knowledge/platform/lint.js +385 -0
- package/dist/knowledge/platform/mobile.js +142 -0
- package/dist/knowledge/platform/mobileLayoutLint.js +324 -0
- package/dist/knowledge/platform/mobileRules.js +231 -0
- package/dist/knowledge/platform/mobileSpacing.js +82 -0
- package/dist/knowledge/platform/mobileTabs.js +108 -0
- package/dist/knowledge/platform/nativeBoxModel.js +44 -0
- package/dist/knowledge/platform/profile.js +25 -0
- package/dist/knowledge/platform/resolve.js +122 -0
- package/dist/knowledge/platform/web.js +39 -0
- package/dist/knowledge/policy.js +18 -4
- package/dist/knowledge/process.js +510 -0
- package/dist/knowledge/reservedTables.js +79 -0
- package/dist/knowledge/rowFilter.js +43 -0
- package/dist/knowledge/rules.js +423 -37
- package/dist/knowledge/schemaGuard.js +11 -0
- package/dist/knowledge/styleResolve.js +53 -19
- package/dist/knowledge/styleResolveNative.js +527 -0
- package/dist/knowledge/themeResolve.js +4 -4
- package/dist/knowledge/themeSchema.js +1 -2
- package/dist/observability/trace.js +6 -3
- package/dist/platform/argumentAliases.js +128 -0
- package/dist/platform/bodyFile.js +103 -0
- package/dist/platform/capture/actions.js +398 -0
- package/dist/platform/capture/browser.js +1 -0
- package/dist/platform/capture/capture.js +683 -0
- package/dist/platform/capture/console-log.js +122 -0
- package/dist/platform/capture/index.js +11 -0
- package/dist/platform/capture/readiness.js +103 -0
- package/dist/platform/capture/summary.js +77 -0
- package/dist/platform/capture/target.js +5 -0
- package/dist/platform/capture/targets/studio-mobile.js +15 -0
- package/dist/platform/capture/targets/studio-web.js +5 -0
- package/dist/platform/diagnostics.js +10 -4
- package/dist/platform/domSummary.js +1 -1
- package/dist/platform/errors.js +163 -10
- package/dist/platform/http.js +29 -8
- package/dist/platform/impersonation.js +45 -0
- package/dist/platform/jsonArray.js +24 -0
- package/dist/platform/responses.js +39 -5
- package/dist/platform/schemaReadMemory.js +60 -0
- package/dist/platform/secrets.js +19 -1
- package/dist/platform/strictArguments.js +68 -12
- package/dist/platform/tokenVault.js +187 -4
- package/dist/resources/examples/bpm-gorev-kontrol.json +294 -0
- package/dist/resources/examples/bpm-iso-dokuman.json +228 -0
- package/dist/resources/examples/bpm-satinalma.json +376 -0
- package/dist/resources/examples/connect-data-convert-verify.json +107 -0
- package/dist/resources/examples/connect-data-dropdown-roles.json +66 -0
- package/dist/resources/examples/connect-data-edit-m2m.json +73 -0
- package/dist/resources/examples/connect-data-query-params.json +50 -0
- package/dist/resources/examples/connect-data-table-entity.json +147 -0
- package/dist/resources/examples/connect-data-workflow-microservice.json +65 -0
- package/dist/resources/examples/middleware-authenticate.json +1 -1
- package/dist/resources/examples/mobile-app-shell-drawer.json +244 -0
- package/dist/resources/examples/mobile-app-shell-stack.json +157 -0
- package/dist/resources/examples/mobile-app-shell-tab.json +208 -0
- package/dist/resources/examples/mobile-form-screen.json +365 -0
- package/dist/resources/examples/mobile-list-detail-form.json +703 -0
- package/dist/resources/examples/page-detail-profile.json +2 -1
- package/dist/resources/examples/page-dynamic-form.json +3 -2
- package/dist/resources/examples/page-flex-sections.json +1 -1
- package/dist/resources/examples/page-html-embed.json +1 -1
- package/dist/resources/examples/workflow-basic.json +8 -2
- package/dist/resources/guides/access.md +810 -0
- package/dist/resources/guides/bpm.md +691 -0
- package/dist/resources/guides/case.md +153 -0
- package/dist/resources/guides/component.md +93 -6
- package/dist/resources/guides/decision.md +185 -0
- package/dist/resources/guides/entity.md +50 -8
- package/dist/resources/guides/expressions.md +14 -5
- package/dist/resources/guides/functions.md +1 -1
- package/dist/resources/guides/getting-started.md +38 -1
- package/dist/resources/guides/layout.md +11 -7
- package/dist/resources/guides/merge.md +37 -3
- package/dist/resources/guides/microservice.md +87 -2
- package/dist/resources/guides/middleware.md +68 -4
- package/dist/resources/guides/mobile-layout.md +193 -0
- package/dist/resources/guides/mobile-publish.md +124 -0
- package/dist/resources/guides/mobile.md +549 -0
- package/dist/resources/guides/page.md +463 -83
- package/dist/resources/guides/theme.md +8 -3
- package/dist/resources/guides/translation.md +1 -1
- package/dist/resources/guides/workflow.md +58 -1
- package/dist/resources/read.js +41 -0
- package/dist/resources/rules.js +11 -5
- package/dist/resources/schema-fingerprint.json +7 -5
- package/dist/resources/schemas/AccessExplanationDto.json +144 -0
- package/dist/resources/schemas/AccessMatrixDto.json +170 -0
- package/dist/resources/schemas/AccessPermissionDto.json +57 -0
- package/dist/resources/schemas/AccessPolicyDto.json +223 -0
- package/dist/resources/schemas/AccessResourceDto.json +86 -0
- package/dist/resources/schemas/AppSessionDto.json +53 -0
- package/dist/resources/schemas/ConnectDataDescribeDto.json +344 -0
- package/dist/resources/schemas/ConnectDataRequestModel.json +202 -0
- package/dist/resources/schemas/ConnectDataResultDto.json +421 -0
- package/dist/resources/schemas/ConnectorCredentialDto.json +70 -0
- package/dist/resources/schemas/ConvertConnectDataToMicroserviceCommand.json +37 -0
- package/dist/resources/schemas/CreateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/CreateDecisionCommand.json +221 -0
- package/dist/resources/schemas/CreateEntityCommand.json +118 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/CreateProcessDefinitionCommand.json +1337 -0
- package/dist/resources/schemas/CreateQueryCommand.json +4 -0
- package/dist/resources/schemas/CreateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/DataJobDto.json +6 -1
- package/dist/resources/schemas/DecisionDto.json +302 -0
- package/dist/resources/schemas/DetachConnectDataMicroserviceCommand.json +28 -0
- package/dist/resources/schemas/ExplainAccessQuery.json +33 -0
- package/dist/resources/schemas/GenerateProcessDocumentCommand.json +116 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +25 -1
- package/dist/resources/schemas/MicroserviceDto.json +15 -3
- package/dist/resources/schemas/OrganizationProjectDto.json +335 -0
- package/dist/resources/schemas/OrganizationSchemaDto.json +196 -0
- package/dist/resources/schemas/OrganizationSchemaVersionDto.json +207 -0
- package/dist/resources/schemas/PatchProcessDefinitionCommand.json +76 -0
- package/dist/resources/schemas/PreviewMicroserviceCommand.json +10 -1
- package/dist/resources/schemas/PreviewWorkflowCommand.json +25 -1
- package/dist/resources/schemas/ProcessDefinitionDto.json +1480 -0
- package/dist/resources/schemas/ProcessSimulationDto.json +96 -0
- package/dist/resources/schemas/SaveConnectorCredentialCommand.json +90 -0
- package/dist/resources/schemas/SaveProcessEventSourceCommand.json +61 -0
- package/dist/resources/schemas/SaveProcessMailSourceCommand.json +89 -0
- package/dist/resources/schemas/SignProcessTaskCommand.json +73 -0
- package/dist/resources/schemas/SimulateProcessDefinitionQuery.json +92 -0
- package/dist/resources/schemas/StartMobileBuildCommand.json +54 -0
- package/dist/resources/schemas/StepDto.json +25 -1
- package/dist/resources/schemas/UpdateAccessPolicyCommand.json +209 -0
- package/dist/resources/schemas/UpdateDecisionCommand.json +216 -0
- package/dist/resources/schemas/UpdateEntityCommand.json +118 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +13 -1
- package/dist/resources/schemas/UpdateProcessDefinitionCommand.json +1357 -0
- package/dist/resources/schemas/UpdateQueryCommand.json +4 -0
- package/dist/resources/schemas/UpdateViewCommand.json +0 -2
- package/dist/resources/schemas/UpdateWorkflowCommand.json +146 -1
- package/dist/resources/schemas/UpsertMobileStoreSettingsCommand.json +274 -0
- package/dist/resources/schemas/ValidateProcessDefinitionQuery.json +1339 -0
- package/dist/server.js +31 -1
- package/dist/tools/access.js +369 -2
- package/dist/tools/accessOperations.js +135 -0
- package/dist/tools/accessPolicies.js +170 -0
- package/dist/tools/accessRuntime.js +203 -0
- package/dist/tools/applications.js +60 -3
- package/dist/tools/bpm.js +343 -0
- package/dist/tools/bpmGovernance.js +266 -0
- package/dist/tools/bpmOperations.js +230 -0
- package/dist/tools/bpmReporting.js +154 -0
- package/dist/tools/bpmRouting.js +150 -0
- package/dist/tools/bpmRuntime.js +396 -0
- package/dist/tools/case.js +178 -0
- package/dist/tools/caseRuntime.js +89 -0
- package/dist/tools/common.js +10 -1
- package/dist/tools/components.js +118 -14
- package/dist/tools/connectData.js +450 -0
- package/dist/tools/connectors.js +193 -0
- package/dist/tools/datasources.js +3 -3
- package/dist/tools/decisions.js +206 -0
- package/dist/tools/dependencies.js +1 -1
- package/dist/tools/deployments.js +38 -3
- package/dist/tools/designSchemaPatch.js +34 -9
- package/dist/tools/dtos.js +36 -5
- package/dist/tools/entities.js +79 -12
- package/dist/tools/externalComponents.js +26 -5
- package/dist/tools/flows.js +119 -17
- package/dist/tools/folders.js +1 -1
- package/dist/tools/imports.js +5 -2
- package/dist/tools/layouts.js +50 -17
- package/dist/tools/merges.js +2 -2
- package/dist/tools/microservices.js +77 -25
- package/dist/tools/mobile.js +280 -0
- package/dist/tools/mobileStore.js +556 -0
- package/dist/tools/modules.js +48 -0
- package/dist/tools/organizationStructure.js +137 -0
- package/dist/tools/pages.js +465 -98
- package/dist/tools/platformInfo.js +28 -8
- package/dist/tools/profileWrite.js +92 -0
- package/dist/tools/projects.js +71 -9
- package/dist/tools/rows.js +69 -13
- package/dist/tools/schemaOps.js +61 -1
- package/dist/tools/screenWrite.js +36 -7
- package/dist/tools/screens.js +95 -5
- package/dist/tools/templates.js +26 -1
- package/dist/tools/translations.js +1 -1
- package/dist/tools/variables.js +11 -5
- package/dist/tools/workflows.js +10 -5
- package/package.json +2 -2
- package/dist/platform/screenshot.js +0 -1162
package/dist/server.js
CHANGED
|
@@ -39,6 +39,20 @@ import { registerMergeTools } from "./tools/merges.js";
|
|
|
39
39
|
import { registerRevisionTools } from "./tools/revisions.js";
|
|
40
40
|
import { registerActivityTools } from "./tools/activities.js";
|
|
41
41
|
import { registerDtoTools } from "./tools/dtos.js";
|
|
42
|
+
import { registerConnectDataTools } from "./tools/connectData.js";
|
|
43
|
+
import { registerBpmTools } from "./tools/bpm.js";
|
|
44
|
+
import { registerBpmRuntimeTools } from "./tools/bpmRuntime.js";
|
|
45
|
+
import { registerBpmOperationsTools } from "./tools/bpmOperations.js";
|
|
46
|
+
import { registerBpmRoutingTools } from "./tools/bpmRouting.js";
|
|
47
|
+
import { registerBpmGovernanceTools } from "./tools/bpmGovernance.js";
|
|
48
|
+
import { registerDecisionTools } from "./tools/decisions.js";
|
|
49
|
+
import { registerCaseRuntimeTools } from "./tools/caseRuntime.js";
|
|
50
|
+
import { registerCaseTools } from "./tools/case.js";
|
|
51
|
+
import { registerConnectorTools } from "./tools/connectors.js";
|
|
52
|
+
import { registerBpmReportingTools } from "./tools/bpmReporting.js";
|
|
53
|
+
import { registerModuleTools } from "./tools/modules.js";
|
|
54
|
+
import { registerMobileTools } from "./tools/mobile.js";
|
|
55
|
+
import { registerMobileStoreTools } from "./tools/mobileStore.js";
|
|
42
56
|
const moduleRegistrations = {
|
|
43
57
|
platform: registerPlatformTools,
|
|
44
58
|
project: registerProjectTools,
|
|
@@ -80,9 +94,25 @@ const moduleRegistrations = {
|
|
|
80
94
|
merge: registerMergeTools,
|
|
81
95
|
activity: registerActivityTools,
|
|
82
96
|
revision: registerRevisionTools,
|
|
97
|
+
connectData: registerConnectDataTools,
|
|
98
|
+
bpm: registerBpmTools,
|
|
99
|
+
bpmRuntime: registerBpmRuntimeTools,
|
|
100
|
+
bpmOperations: registerBpmOperationsTools,
|
|
101
|
+
bpmRouting: registerBpmRoutingTools,
|
|
102
|
+
bpmGovernance: registerBpmGovernanceTools,
|
|
103
|
+
decision: registerDecisionTools,
|
|
104
|
+
connector: registerConnectorTools,
|
|
105
|
+
bpmReporting: registerBpmReportingTools,
|
|
106
|
+
case: registerCaseTools,
|
|
107
|
+
caseRuntime: registerCaseRuntimeTools,
|
|
108
|
+
module: registerModuleTools,
|
|
109
|
+
mobile: (server, platform) => {
|
|
110
|
+
registerMobileTools(server, platform);
|
|
111
|
+
registerMobileStoreTools(server, platform);
|
|
112
|
+
},
|
|
83
113
|
};
|
|
84
114
|
export function createGatewayServer(platform) {
|
|
85
|
-
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config));
|
|
115
|
+
const server = rejectUnknownArguments(instrumentServer(new McpServer({ name: "ngapps", version: "0.1.0" }), platform.config), platform);
|
|
86
116
|
for (const moduleName of platform.config.modules) {
|
|
87
117
|
moduleRegistrations[moduleName](server, platform);
|
|
88
118
|
}
|
package/dist/tools/access.js
CHANGED
|
@@ -1,11 +1,81 @@
|
|
|
1
|
-
import { projectParam, environmentParam, pagingParams, readOnly } from './common.js';
|
|
1
|
+
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
2
3
|
import { handle } from '../platform/responses.js';
|
|
4
|
+
import { registerOrganizationStructureTools } from './organizationStructure.js';
|
|
5
|
+
import { registerAccessOperationTools } from './accessOperations.js';
|
|
6
|
+
import { registerAccessPolicyTools } from './accessPolicies.js';
|
|
7
|
+
import { registerAccessRuntimeTools } from './accessRuntime.js';
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
3
9
|
import { z } from 'zod';
|
|
10
|
+
function count(value) {
|
|
11
|
+
return Array.isArray(value) ? value.length : 0;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Gateway'in jetonu organizasyonu yalnız SLUG olarak taşır (`UserService.SignInAsync` claim'i
|
|
15
|
+
* `organization`) ve `/connect/userinfo` organizasyon id'si döndürmez; şema uçları ise id ister.
|
|
16
|
+
* Id `ORGANIZATION` ayarındaki slug ile organizasyon listesinden bulunur.
|
|
17
|
+
*/
|
|
18
|
+
export async function currentOrganizationId(platform, tool = 'organization_schema_get') {
|
|
19
|
+
const slug = platform.config.organization;
|
|
20
|
+
// Üç tuzak (OrganizationRepository.FilterAsync + OrganizationDto, canlı ölçüm 2026-09-15):
|
|
21
|
+
// `includeDefault` olmadan liste varsayılan organizasyonu GİZLER — yerel kurulumun organizasyonu
|
|
22
|
+
// tam da odur —, `searchTerm` adda arar ve DTO'da `slug` yoktur: `ORGANIZATION` host'un alt
|
|
23
|
+
// alan adıdır (`subDomain`). Arama terimi gönderilmez, eşleşme `subDomain` ile yapılır.
|
|
24
|
+
// Liste ucu `organization.read` ister (OrganizationsController.Get), şema ucu yalnız
|
|
25
|
+
// `organization.schema.read`: şemayı okuyabilen kullanıcı listeyi okuyamayabilir.
|
|
26
|
+
const payload = await platform.org('GET', '/organizations', { query: { pageLength: 200, includeDefault: true } }).catch((error) => {
|
|
27
|
+
if (error instanceof PlatformApiError && error.status === 403) {
|
|
28
|
+
throw new PlatformApiError(`${tool}: organizasyon id'si bulunamadı — gateway id'yi GET /organizations listesinden okur, o uç 'organization.read' izni ister ve bu kullanıcıda yok (şema ucu yalnız 'organization.schema.read' ister). organizationId'yi açıkça verin.`, 403, error.code);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
});
|
|
32
|
+
const found = (payload?.data ?? []).find(entry => entry.subDomain === slug || entry.slug === slug);
|
|
33
|
+
if (typeof found?.id !== 'string') {
|
|
34
|
+
throw new PlatformApiError(`${tool}: '${slug}' slug'lı organizasyon listede bulunamadı (GET /organizations). organizationId'yi açıkça verin.`, 404);
|
|
35
|
+
}
|
|
36
|
+
return found.id;
|
|
37
|
+
}
|
|
38
|
+
const unitTypes = ['holding', 'company', 'branch', 'region', 'department', 'team', 'other'];
|
|
39
|
+
const draftWriteNote = "Yazma YALNIZ TASLAĞA gider (PUT /organizations/{id}/schema); hiçbir projeye inmez ve production'a dokunmaz. Abone projeler ancak organization_schema_publish ile güncellenir ve o çağrı production'a yazdığı için AI'ın işi değildir — taslağı AI yazar, YAYINI İNSAN yapar. Farkı yayından önce organization_schema_diff ile okuyun: `from` YAYINLANMIŞ bir sürüm (genelde publishedVersion), `to` verilmezse taslakla karşılaştırır. Organizasyonda hiç yayın yoksa taban da yoktur ve diff 404 döner — o durumda taslağı organization_schema_get ile okuyun.";
|
|
40
|
+
function withoutUndefined(input) {
|
|
41
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
42
|
+
}
|
|
43
|
+
function draftList(value) {
|
|
44
|
+
return Array.isArray(value) ? value : [];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Taslak tek parça yazılır: platformda düğüm başına uç YOKTUR, yönetim konsolu da üç listeyi
|
|
48
|
+
* olduğu gibi gönderir (`PUT /organizations/{id}/schema`). Tek düğüm ekleyen araçlar bu yüzden
|
|
49
|
+
* önce taslağı okur, listeyi değiştirir ve tamamını geri yazar — arada başkası yazdıysa onun
|
|
50
|
+
* değişikliği ezilir (bilinçli sınır: taslakta eşzamanlılık jetonu yok, TB-104 ile aynı çizgi).
|
|
51
|
+
*/
|
|
52
|
+
async function readSchemaDraft(platform, organizationId) {
|
|
53
|
+
const payload = await platform.org('GET', `/organizations/${organizationId}/schema`);
|
|
54
|
+
const data = payload?.data ?? {};
|
|
55
|
+
return { units: draftList(data.units), positions: draftList(data.positions), roles: draftList(data.roles) };
|
|
56
|
+
}
|
|
57
|
+
async function saveSchemaDraft(platform, organizationId, draft, change, writtenId) {
|
|
58
|
+
const payload = await platform.org('PUT', `/organizations/${organizationId}/schema`, { body: draft });
|
|
59
|
+
const data = (payload?.data ?? {});
|
|
60
|
+
return {
|
|
61
|
+
...payload,
|
|
62
|
+
summary: {
|
|
63
|
+
organizationId,
|
|
64
|
+
...(writtenId === undefined ? {} : { writtenId }),
|
|
65
|
+
draftVersion: data.draftVersion ?? 0,
|
|
66
|
+
publishedVersion: data.publishedVersion ?? null,
|
|
67
|
+
units: draftList(data.units).length,
|
|
68
|
+
positions: draftList(data.positions).length,
|
|
69
|
+
roles: draftList(data.roles).length
|
|
70
|
+
},
|
|
71
|
+
note: `${change} Değişiklik TASLAKTA durur; abone projelere inmesi için insanın organization_schema_publish çağırması gerekir.`
|
|
72
|
+
};
|
|
73
|
+
}
|
|
4
74
|
export function registerAccessTools(server, platform) {
|
|
5
75
|
const config = platform.config;
|
|
6
76
|
server.registerTool('role_list', {
|
|
7
77
|
title: 'Rolleri listele',
|
|
8
|
-
description: "Projenin rollerini listeler. Uç YALNIZ id + name döner (backend RoleDto bu ikisini taşır) — SLUG BURADA YOKTUR ve iki anahtar farklıdır: page_create/page_update ve microservice_create'in roleIds alanı ID ister (microservice_patch'te aynı liste '/roles' yolundadır ve öğeleri { id, name } çiftidir — yamalanan belge okuma şeklidir), buna karşılık Authorized(user.roles) ile middleware'in kurduğu user.roles SLUG karşılaştırır (platformun get-user sorgusu roles+user_roles join'inden slug dizisi üretir). Slug'ı built-in 'roles' entity'sinden okuyun (sabit id 01917ede-88a2-72ea-8397-2619e5d5dca0; row_list/microservice). ROL OLUŞTURAN BİR TOOL YOKTUR ve olmamalıdır: roller built-in datasource'taki sıradan bir entity'dir (kolonlar id · name UNIQUE · slug). Uygulamanın rol yönetmesi gerekiyorsa yol entity_get('roles') + kendi microservice ucun (uç + sayfa) ve rolü kullanıcıya bağlayan user_roles satırı — MCP'den doğrudan yazma değil; reçete platform_guide('entity') §'Hazır entity'ler'. Projeyle birlikte gelen roller çoğu senaryoya yeter; önce bu listeye bakın.",
|
|
78
|
+
description: "Projenin rollerini listeler. Uç YALNIZ id + name döner (backend RoleDto bu ikisini taşır) — SLUG BURADA YOKTUR ve iki anahtar farklıdır: page_create/page_update ve microservice_create'in roleIds alanı ID ister (microservice_patch'te aynı liste '/roles' yolundadır ve öğeleri { id, name } çiftidir — yamalanan belge okuma şeklidir), buna karşılık Authorized(user.roles) ile middleware'in kurduğu user.roles SLUG karşılaştırır (platformun get-user sorgusu roles+user_roles join'inden slug dizisi üretir). Slug'ı built-in 'roles' entity'sinden okuyun (sabit id 01917ede-88a2-72ea-8397-2619e5d5dca0; row_list/microservice). ROL OLUŞTURAN BİR TOOL YOKTUR ve olmamalıdır: roller built-in datasource'taki sıradan bir entity'dir (kolonlar id · name UNIQUE · slug · origin). Uygulamanın rol yönetmesi gerekiyorsa yol entity_get('roles') + kendi microservice ucun (uç + sayfa) ve rolü kullanıcıya bağlayan user_roles satırı — MCP'den doğrudan yazma değil; reçete platform_guide('entity') §'Hazır entity'ler'. Projeyle birlikte gelen roller çoğu senaryoya yeter; önce bu listeye bakın. KOMPLEKS PROJEDE roller organizasyon şemasından gelir (roles.origin = 'organization'; bu uç origin'i döndürmez, row_list('roles') ile okuyun): şemadan gelen rol MCP'den yazılmaz, yönetim konsolunda düzenlenir; yetki page.roleIds ile değil access policy ile verilir (organization_schema_get).",
|
|
9
79
|
inputSchema: { project: projectParam, environment: environmentParam },
|
|
10
80
|
annotations: readOnly
|
|
11
81
|
}, handle(config, async ({ project, environment }) => {
|
|
@@ -18,10 +88,307 @@ export function registerAccessTools(server, platform) {
|
|
|
18
88
|
inputSchema: { project: projectParam, environment: environmentParam, searchTerm: z.string().optional(), ...pagingParams },
|
|
19
89
|
annotations: readOnly
|
|
20
90
|
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/users', { environment, query })));
|
|
91
|
+
server.registerTool('user_create', {
|
|
92
|
+
title: 'Proje kullanıcısı oluştur',
|
|
93
|
+
description: "Projeye kullanıcı açar (POST /{project}/users). Kompleks projede kurulumun ilk adımıdır: kullanıcı önce burada doğar, sonra user_position_replace ile bir POZİSYONA oturtulur — yetkiyi veren koltuktur, buradaki roleIds değil (ACC-008). Kompleks projede doğrudan atanan rol HİÇ sayılmaz: özne rolleri yalnız pozisyondan gelir (S-15, 2026-09-21) — koltuğu olmayan kullanıcı rolsüzdür. Birleşim yalnız basit projede sürer; bkz. platform_guide('access') §Eski rol listeleri. Parola gateway günlüğüne maskeli yazılır ve yanıta girmez.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
project: projectParam,
|
|
96
|
+
environment: environmentParam,
|
|
97
|
+
id: guid.optional().describe('Verilmezse platform üretir'),
|
|
98
|
+
email: z.string().email(),
|
|
99
|
+
password: z.string().min(8).describe('En az 8 karakter (platform doğrulaması)'),
|
|
100
|
+
firstName: z.string().min(1),
|
|
101
|
+
lastName: z.string().min(1),
|
|
102
|
+
roleIds: z.array(guid).optional().describe("role_list id'leri; kompleks projede boş bırakın ve yetkiyi pozisyondan verin")
|
|
103
|
+
},
|
|
104
|
+
annotations: writes
|
|
105
|
+
}, handle(config, ({ project, environment, ...body }) => platform.tenant('POST', project, '/users', { environment, body })));
|
|
106
|
+
server.registerTool('user_update', {
|
|
107
|
+
title: 'Proje kullanıcısını güncelle',
|
|
108
|
+
description: "Kullanıcının adını, parolasını ve rollerini değiştirir (PUT /{project}/users/{id}). E-posta DEĞİŞTİRİLEMEZ — gövdede yoktur. roleIds verilirse liste REPLACE edilir; dokunmak istemiyorsanız alanı hiç göndermeyin. KOMPLEKS projede bu liste yetki VERMEZ (S-15): rol pozisyondan gelir, yetkiyi user_position_replace ile verin. Parola boş bırakılırsa değişmez, verilirse en az 8 karakter olmalıdır.",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
project: projectParam,
|
|
111
|
+
environment: environmentParam,
|
|
112
|
+
id: guid,
|
|
113
|
+
firstName: z.string().min(1),
|
|
114
|
+
lastName: z.string().min(1),
|
|
115
|
+
password: z.string().min(8).optional().describe('Yalnız değiştirmek istiyorsanız; günlüğe maskeli yazılır'),
|
|
116
|
+
roleIds: z.array(guid).optional()
|
|
117
|
+
},
|
|
118
|
+
annotations: updates
|
|
119
|
+
}, handle(config, ({ project, environment, id, ...body }) => platform.tenant('PUT', project, `/users/${id}`, { environment, body })));
|
|
120
|
+
server.registerTool('user_delete', {
|
|
121
|
+
title: 'Proje kullanıcısını sil',
|
|
122
|
+
description: "Kullanıcıyı projeden siler (DELETE /{project}/users/{id}). Silme serttir: pozisyon atamaları (koltukları) ve doğrudan politika atamaları kullanıcıyla birlikte düşer — hangi koltukların boşalacağını önceden görmek için user_position_list; ürettiği denetim kayıtları kalır. Sahip olduğu satırlar silinmez, YALNIZ KOMPLEKS projede sahiplikleri çözülür (basit modelde bu adım hiç çalışmaz): transferToUserId verilmezse sys_owner_user_id boşalır, sys_owner_unit_id kalır ve entity başına bir OWNER-RELEASED denetim kaydı yazılır (TB-106). transferToUserId verilirse satırlar devralana geçer — o zaman sys_owner_unit_id de devralanın birincil aktif pozisyonunun birimine yazılır (devralanın aktif pozisyonu yoksa birim hiç değişmez) ve kayıt OWNER-RELEASED değil REASSIGN olur. Sonucu entity_audit_trails gösterir.",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
project: projectParam,
|
|
125
|
+
environment: environmentParam,
|
|
126
|
+
id: guid,
|
|
127
|
+
transferToUserId: guid.optional().describe('Devralan kullanıcı: silinenin satırları boşa düşmek yerine buna geçer; silinen kişinin kendisi olamaz. Devralan yoksa 404 gelir ve o 404 silinecek kullanıcıyı değil DEVRALANI gösteriyor olabilir — kullanıcı silinmemiştir')
|
|
128
|
+
},
|
|
129
|
+
annotations: destructive
|
|
130
|
+
}, handle(config, ({ project, environment, id, transferToUserId }) => platform.tenant('DELETE', project, `/users/${id}`, { environment, query: { transferToUserId } })));
|
|
21
131
|
server.registerTool('org_role_list', {
|
|
22
132
|
title: 'Organizasyon rollerini listele',
|
|
23
133
|
description: 'Organization.API tarafındaki rolleri listeler (platform yönetimi); proje içi yetkilendirme için role_list kullanın. Burada da oluşturma yoktur — organizasyon rolleri platform yönetimine aittir ve uygulamanın rol tablosuyla (built-in roles entity) ilgisi yoktur.',
|
|
24
134
|
inputSchema: {},
|
|
25
135
|
annotations: readOnly
|
|
26
136
|
}, handle(config, () => platform.org('GET', '/roles')));
|
|
137
|
+
server.registerTool('organization_schema_get', {
|
|
138
|
+
title: 'Organizasyon şemasını getir',
|
|
139
|
+
description: "Organizasyonun rol yönetim şemasını (birim · pozisyon · rol ağacı) salt okunur getirir: version verilmezse taslak (draftVersion) + son yayın numarası (publishedVersion), verilirse o yayının anlık görüntüsü. Kompleks proje bu şemanın yayınlı son sürümüyle doğar (ORG-002). TASLAĞA yazmak için organization_schema_unit_create/update ve organization_schema_position_create/update; YAYIN insanın işidir (organization_schema_publish production'a yazar). Projenin kendi kopyası hazır entity'lerdedir — row_list('organization_units').",
|
|
140
|
+
inputSchema: {
|
|
141
|
+
organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la organizasyon listesinden bulur"),
|
|
142
|
+
version: z.number().int().min(1).optional().describe('Yayın sürümü (organization_schema_get yanıtındaki publishedVersion ya da project_get organizationSchema.appliedVersion)')
|
|
143
|
+
},
|
|
144
|
+
annotations: readOnly
|
|
145
|
+
}, handle(config, async ({ organizationId, version }) => {
|
|
146
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
147
|
+
const path = `/organizations/${resolvedId}/schema${version === undefined ? '' : `/versions/${version}`}`;
|
|
148
|
+
const payload = await platform.org('GET', path);
|
|
149
|
+
const data = (payload?.data ?? {});
|
|
150
|
+
const lists = (version === undefined ? data : (data.snapshot ?? {}));
|
|
151
|
+
const publishedVersion = typeof data.publishedVersion === 'number' ? data.publishedVersion : undefined;
|
|
152
|
+
return {
|
|
153
|
+
...payload,
|
|
154
|
+
summary: {
|
|
155
|
+
organizationId: resolvedId,
|
|
156
|
+
...(version === undefined
|
|
157
|
+
? { draftVersion: data.draftVersion ?? 0, published: publishedVersion !== undefined, ...(publishedVersion !== undefined ? { publishedVersion } : {}) }
|
|
158
|
+
: { version }),
|
|
159
|
+
units: count(lists.units),
|
|
160
|
+
positions: count(lists.positions),
|
|
161
|
+
roles: count(lists.roles)
|
|
162
|
+
},
|
|
163
|
+
note: version === undefined
|
|
164
|
+
? (publishedVersion === undefined
|
|
165
|
+
? "Organizasyonda yayınlı şema YOK: bu anda açılan kompleks proje boş organizasyon tablolarıyla doğar (ORG-002). Taslak yönetim konsolunda yayınlanmalı."
|
|
166
|
+
: "Dönen listeler TASLAKTIR; projelere giden yayınlı sürümün içeriği için version: publishedVersion ile çağırın.")
|
|
167
|
+
: 'Yayının donmuş anlık görüntüsü (snapshot); abone projeler bu sürümü taşır.'
|
|
168
|
+
};
|
|
169
|
+
}));
|
|
170
|
+
server.registerTool('organization_schema_unit_create', {
|
|
171
|
+
title: 'Şema taslağına birim ekle',
|
|
172
|
+
description: `Organizasyon şemasının TASLAĞINA yeni bir birim ekler. ${draftWriteNote} unitType yalnız ETİKETTİR: platform ebeveyn tipini denetlemez (şube doğrudan şirketin altına alınabilir), yalnız döngü ve tek kök denetlenir (TB-111). Şirket sınırı 'company' tipli birimdir ve kapsam hesabında companyIds odur. Taslak düğümün id'si yanıtta döner; pozisyon açarken unitId olarak onu verin.`,
|
|
173
|
+
inputSchema: {
|
|
174
|
+
organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la bulur"),
|
|
175
|
+
name: z.string().min(1),
|
|
176
|
+
code: z.string().min(1).describe('Şemada tekil kısa kod'),
|
|
177
|
+
unitType: z.enum(unitTypes),
|
|
178
|
+
parentId: guid.optional().describe("Üst birimin taslaktaki id'si; verilmezse kök"),
|
|
179
|
+
functionCode: z.string().optional().describe("İşlev etiketi; grant koşulunda { subject: 'functionCodes' } ile eşleşir"),
|
|
180
|
+
managerPositionId: guid.optional(),
|
|
181
|
+
sortOrder: z.number().int().optional()
|
|
182
|
+
},
|
|
183
|
+
annotations: writes
|
|
184
|
+
}, handle(config, async ({ organizationId, ...input }) => {
|
|
185
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
186
|
+
const draft = await readSchemaDraft(platform, resolvedId);
|
|
187
|
+
const unit = { id: randomUUID(), parentId: null, functionCode: null, managerPositionId: null, sortOrder: 0, ...withoutUndefined(input) };
|
|
188
|
+
return saveSchemaDraft(platform, resolvedId, { ...draft, units: [...draft.units, unit] }, `Taslağa '${input.name}' birimi eklendi (id ${unit.id}).`, unit.id);
|
|
189
|
+
}));
|
|
190
|
+
server.registerTool('organization_schema_unit_update', {
|
|
191
|
+
title: 'Şema taslağındaki birimi güncelle',
|
|
192
|
+
description: `Taslaktaki bir birimin alanlarını değiştirir. ${draftWriteNote} Verilmeyen alan KORUNUR (gateway taslağı okur, tek düğümü değiştirir, listeyi geri yazar) — projedeki organization_unit_update'ten farkı budur, o tam gövde ister. parentId vererek düğümü taşıyabilirsiniz; döngü kuran taşıma platformda reddedilir.`,
|
|
193
|
+
inputSchema: {
|
|
194
|
+
organizationId: guid.optional(),
|
|
195
|
+
id: guid.describe("Birimin taslaktaki id'si (organization_schema_get)"),
|
|
196
|
+
name: z.string().min(1).optional(),
|
|
197
|
+
code: z.string().min(1).optional(),
|
|
198
|
+
unitType: z.enum(unitTypes).optional(),
|
|
199
|
+
parentId: guid.nullable().optional().describe('Yeni üst birim; null kökle eşdeğerdir'),
|
|
200
|
+
functionCode: z.string().nullable().optional(),
|
|
201
|
+
managerPositionId: guid.nullable().optional(),
|
|
202
|
+
sortOrder: z.number().int().optional()
|
|
203
|
+
},
|
|
204
|
+
annotations: updates
|
|
205
|
+
}, handle(config, async ({ organizationId, id, ...input }) => {
|
|
206
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
207
|
+
const draft = await readSchemaDraft(platform, resolvedId);
|
|
208
|
+
const existing = draft.units.find(unit => unit.id === id);
|
|
209
|
+
if (!existing)
|
|
210
|
+
throw new PlatformApiError(`organization_schema_unit_update: '${id}' taslakta yok. organization_schema_get ile taslaktaki birimleri okuyun.`, 404);
|
|
211
|
+
const units = draft.units.map(unit => unit.id === id ? { ...unit, ...withoutUndefined(input) } : unit);
|
|
212
|
+
return saveSchemaDraft(platform, resolvedId, { ...draft, units }, `Taslakta '${existing.code}' birimi güncellendi.`);
|
|
213
|
+
}));
|
|
214
|
+
server.registerTool('organization_schema_position_create', {
|
|
215
|
+
title: 'Şema taslağına pozisyon ekle',
|
|
216
|
+
description: `Organizasyon şemasının TASLAĞINA yeni bir pozisyon (koltuk) ekler. ${draftWriteNote} Pozisyon şemadaki bir ROLE bağlıdır (roleId — organization_schema_get 'roles'); kullanıcının rolü kompleks projede pozisyonundan gelir. parentPositionId idari, functionalParentPositionId işlevsel hattı kurar ve 'subordinates' kapsamı bu hatlardan yürür.`,
|
|
217
|
+
inputSchema: {
|
|
218
|
+
organizationId: guid.optional(),
|
|
219
|
+
unitId: guid.describe("Pozisyonun bağlı olduğu taslak birimi"),
|
|
220
|
+
roleId: guid.describe("Şemadaki rolün id'si (organization_schema_get 'roles')"),
|
|
221
|
+
name: z.string().min(1),
|
|
222
|
+
code: z.string().min(1).describe('Şemada tekil kısa kod'),
|
|
223
|
+
parentPositionId: guid.optional().describe('İdari üst pozisyon'),
|
|
224
|
+
functionalParentPositionId: guid.optional().describe('İşlevsel üst pozisyon'),
|
|
225
|
+
isMultiSeat: z.boolean().optional().describe('Varsayılan false: koltukta aynı anda tek KALICI kişi oturur')
|
|
226
|
+
},
|
|
227
|
+
annotations: writes
|
|
228
|
+
}, handle(config, async ({ organizationId, ...input }) => {
|
|
229
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
230
|
+
const draft = await readSchemaDraft(platform, resolvedId);
|
|
231
|
+
const position = { id: randomUUID(), parentPositionId: null, functionalParentPositionId: null, isMultiSeat: false, ...withoutUndefined(input) };
|
|
232
|
+
return saveSchemaDraft(platform, resolvedId, { ...draft, positions: [...draft.positions, position] }, `Taslağa '${input.name}' pozisyonu eklendi (id ${position.id}).`, position.id);
|
|
233
|
+
}));
|
|
234
|
+
server.registerTool('organization_schema_position_update', {
|
|
235
|
+
title: 'Şema taslağındaki pozisyonu güncelle',
|
|
236
|
+
description: `Taslaktaki bir pozisyonun alanlarını değiştirir. ${draftWriteNote} Verilmeyen alan KORUNUR. Rolü değiştirmek yayından sonra o koltuktaki HER kullanıcının yetkisini değiştirir.`,
|
|
237
|
+
inputSchema: {
|
|
238
|
+
organizationId: guid.optional(),
|
|
239
|
+
id: guid.describe("Pozisyonun taslaktaki id'si (organization_schema_get)"),
|
|
240
|
+
unitId: guid.optional(),
|
|
241
|
+
roleId: guid.optional(),
|
|
242
|
+
name: z.string().min(1).optional(),
|
|
243
|
+
code: z.string().min(1).optional(),
|
|
244
|
+
parentPositionId: guid.nullable().optional(),
|
|
245
|
+
functionalParentPositionId: guid.nullable().optional(),
|
|
246
|
+
isMultiSeat: z.boolean().optional()
|
|
247
|
+
},
|
|
248
|
+
annotations: updates
|
|
249
|
+
}, handle(config, async ({ organizationId, id, ...input }) => {
|
|
250
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
251
|
+
const draft = await readSchemaDraft(platform, resolvedId);
|
|
252
|
+
const existing = draft.positions.find(position => position.id === id);
|
|
253
|
+
if (!existing)
|
|
254
|
+
throw new PlatformApiError(`organization_schema_position_update: '${id}' taslakta yok. organization_schema_get ile taslaktaki pozisyonları okuyun.`, 404);
|
|
255
|
+
const positions = draft.positions.map(position => position.id === id ? { ...position, ...withoutUndefined(input) } : position);
|
|
256
|
+
return saveSchemaDraft(platform, resolvedId, { ...draft, positions }, `Taslakta '${existing.code}' pozisyonu güncellendi.`);
|
|
257
|
+
}));
|
|
258
|
+
server.registerTool('organization_schema_versions', {
|
|
259
|
+
title: 'Şema yayınlarını listele',
|
|
260
|
+
description: "Organizasyon şemasının yayın geçmişini listeler (GET /organizations/{organizationId}/schema/versions): sürüm numarası, yayın notu, yayınlayan ve tarih. Bir projenin hangi sürümü taşıdığını project_get organizationSchema.appliedVersion söyler; aradaki farkı organization_schema_diff verir.",
|
|
261
|
+
inputSchema: {
|
|
262
|
+
organizationId: guid.optional().describe("Verilmezse gateway ORGANIZATION ayarındaki slug'la bulur"),
|
|
263
|
+
pageIndex: z.number().int().min(0).optional(),
|
|
264
|
+
pageLength: z.number().int().min(1).max(1000).optional()
|
|
265
|
+
},
|
|
266
|
+
annotations: readOnly
|
|
267
|
+
}, handle(config, async ({ organizationId, ...query }) => {
|
|
268
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
269
|
+
return platform.org('GET', `/organizations/${resolvedId}/schema/versions`, { query });
|
|
270
|
+
}));
|
|
271
|
+
server.registerTool('organization_schema_diff', {
|
|
272
|
+
title: 'İki şema yayınını karşılaştır',
|
|
273
|
+
description: "İki yayın arasındaki birim/pozisyon/rol farkını verir (GET /organizations/{organizationId}/schema/diff): eklenen, silinen ve değişen düğümler. Bir projeyi yeni sürüme almadan önce 'ne değişecek' sorusunun cevabı budur — özellikle silinen birim/pozisyon, o düğüme dayanan grant'leri sahipsiz bırakır.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
organizationId: guid.optional(),
|
|
276
|
+
from: z.number().int().min(1).describe("Karşılaştırmanın başlangıç sürümü — YAYINLANMIŞ bir sürüm olmalı (sürümler 1'den başlar); yayınlanmamış bir numara 404 döner. Organizasyonda hiç yayın yoksa karşılaştırılacak taban da yoktur: taslağı organization_schema_get ile okuyun"),
|
|
277
|
+
to: z.number().int().min(1).optional().describe('Karşılaştırmanın bitiş sürümü; VERİLMEZSE yayınlanmamış TASLAKLA karşılaştırır — taslağa yazdıktan sonra "yayın neyi değiştirecek" sorusunun cevabı budur')
|
|
278
|
+
},
|
|
279
|
+
annotations: readOnly
|
|
280
|
+
}, handle(config, async ({ organizationId, from, to }) => {
|
|
281
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
282
|
+
return platform.org('GET', `/organizations/${resolvedId}/schema/diff`, { query: { from, ...(to === undefined ? {} : { to }) } });
|
|
283
|
+
}));
|
|
284
|
+
server.registerTool('organization_schema_subscribers', {
|
|
285
|
+
title: 'Şema abonelerini listele',
|
|
286
|
+
description: "Organizasyon şemasına abone projeleri listeler (GET /organizations/{organizationId}/schema/subscribers): her projenin uyguladığı sürüm, aboneliğinin açık olup olmadığı ve özelleştirilmiş olup olmadığı. Bir yayının nereye ineceğini yayından ÖNCE buradan görürsünüz.",
|
|
287
|
+
inputSchema: { organizationId: guid.optional() },
|
|
288
|
+
annotations: readOnly
|
|
289
|
+
}, handle(config, async ({ organizationId }) => {
|
|
290
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
291
|
+
return platform.org('GET', `/organizations/${resolvedId}/schema/subscribers`);
|
|
292
|
+
}));
|
|
293
|
+
server.registerTool('organization_schema_publish', {
|
|
294
|
+
title: 'Şema taslağını yayınla',
|
|
295
|
+
description: "Organizasyon şemasının TASLAĞINI yeni bir sürüm olarak yayınlar (POST /organizations/{organizationId}/schema/publish). GERİ ALINAMAZ ve GENİŞ ETKİLİDİR: yayın bütün abone projelere senkronize edilir ve abone projenin PRODUCTION şemasına da yazar — deployment beklemez. Yayından önce organization_schema_subscribers ile kimin etkileneceğini, organization_schema_diff ile neyin değişeceğini okuyun. Taslak yönetim konsolunda düzenlenir; bu araç yalnız yayınlar.",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
organizationId: guid.optional(),
|
|
298
|
+
note: z.string().max(1000).optional().describe('Yayın notu (sürüm geçmişinde görünür)')
|
|
299
|
+
},
|
|
300
|
+
annotations: writes
|
|
301
|
+
}, handle(config, async ({ organizationId, note }) => {
|
|
302
|
+
const resolvedId = organizationId ?? await currentOrganizationId(platform);
|
|
303
|
+
const payload = await platform.org('POST', `/organizations/${resolvedId}/schema/publish`, { body: note === undefined ? {} : { note } });
|
|
304
|
+
return { ...payload, note: 'Yayın abone projelere senkronize ediliyor; ilerlemeyi project_organization_schema_tasks ile izleyin.' };
|
|
305
|
+
}));
|
|
306
|
+
server.registerTool('project_organization_schema_tasks', {
|
|
307
|
+
title: 'Projenin şema senkron görevleri',
|
|
308
|
+
description: "Bir projenin organizasyon şeması senkron görevlerini listeler (GET /projects/{id}/organization-schema/sync-tasks): environment başına sürüm, durum (pending · processing · completed · skipped-customized · error), deneme sayısı ve hata. Yayının o projeye GERÇEKTEN indiğinin kanıtı budur; 'skipped-customized' proje özelleştirildiği için atlandığını söyler (organization_schema_resubscribe gerekir).",
|
|
309
|
+
inputSchema: { projectId: guid },
|
|
310
|
+
annotations: readOnly
|
|
311
|
+
}, handle(config, ({ projectId }) => platform.org('GET', `/projects/${projectId}/organization-schema/sync-tasks`)));
|
|
312
|
+
server.registerTool('organization_schema_resubscribe_preview', {
|
|
313
|
+
title: 'Yeniden abonelik önizlemesi',
|
|
314
|
+
description: "Özelleştirilmiş bir projeyi şemaya yeniden abone etmenin ne yapacağını ÖNCEDEN gösterir (POST /projects/{id}/organization-schema/resubscribe/preview): şemadan gelecek düğümler ve projede elle açılmış (custom) düğümler. Yan etkisizdir; uygulamak için organization_schema_resubscribe.",
|
|
315
|
+
inputSchema: { projectId: guid },
|
|
316
|
+
annotations: readOnly
|
|
317
|
+
}, handle(config, ({ projectId }) => platform.org('POST', `/projects/${projectId}/organization-schema/resubscribe/preview`)));
|
|
318
|
+
server.registerTool('organization_schema_drift', {
|
|
319
|
+
title: 'Proje organizasyon sapma raporu',
|
|
320
|
+
description: "Projenin YEREL organizasyon kopyası ile merkezi şema arasındaki farkı ortam ortam verir (GET /projects/{id}/organization-schema/drift) — 'projede ne değişti, şemada sonradan ne değişti' sorusunun cevabı budur (TB-112). İki ekseni AYIRIR: driftFromApplied = projenin UYGULADIĞI sürüme göre yerel kopyadaki fark (updated = proje bir şema düğümünü yeniden adlandırdı/taşıdı, removed = proje onu ARŞİVLEDİ, added = yerelde olup o sürümde olmayan şema düğümü); newerInSchema = aynı uygulanmış sürümden son YAYIMLANMIŞ sürüme kadar şemada olan değişiklikler. customNodes projede elle açılmış (origin=project) düğümlerdir ve sapma sayılmaz. organization_schema_diff iki YAYIMLANMIŞ sürümü karşılaştırır ve projenin kopyasını hiç görmez; bu uç tam tersine projeye bakar. Yan etkisizdir. Yalnız kompleks projede çalışır (basit projede 400).",
|
|
321
|
+
inputSchema: { projectId: guid },
|
|
322
|
+
annotations: readOnly
|
|
323
|
+
}, handle(config, ({ projectId }) => platform.org('GET', `/projects/${projectId}/organization-schema/drift`)));
|
|
324
|
+
server.registerTool('organization_schema_resubscribe', {
|
|
325
|
+
title: 'Projeyi şemaya yeniden abone et',
|
|
326
|
+
description: "Özelleştirilmiş bir projeyi organizasyon şemasına yeniden abone eder (POST /projects/{id}/organization-schema/resubscribe) ve projenin ağacını yayınlı son sürümle hizalar. customNodePolicy projede elle açılmış düğümlere ne olacağını söyler: 'keep' korur, 'deactivate' pasifleştirir. Etkisini önce organization_schema_resubscribe_preview ile okuyun — hizalama pozisyon silerse o pozisyona bağlı kullanıcı atamaları da düşer.",
|
|
327
|
+
inputSchema: {
|
|
328
|
+
projectId: guid,
|
|
329
|
+
customNodePolicy: z.enum(['keep', 'deactivate']).optional().describe("Projede elle açılmış düğümlerin akıbeti; varsayılan 'keep'")
|
|
330
|
+
},
|
|
331
|
+
annotations: updates
|
|
332
|
+
}, handle(config, ({ projectId, customNodePolicy }) => platform.org('POST', `/projects/${projectId}/organization-schema/resubscribe`, { body: customNodePolicy === undefined ? {} : { customNodePolicy } })));
|
|
333
|
+
server.registerTool('access_model_preflight', {
|
|
334
|
+
title: 'Erişim modeli dönüşüm ön denetimi',
|
|
335
|
+
description: "Bir projeyi basit ↔ kompleks modele çevirmeden ÖNCE ortam ortam ölçüm yapar (POST /projects/{id}/access-model/preflight): entity ve satır sayıları, sahiplik alanı olmayan entity'ler, dış datasource'a bağlı entity'ler, sorgu ve kısıtsız sorgu sayıları, rol sayısı, rezerve tablo adı çakışmaları, sys_created_user_id'den doldurulabilir satır sayısı, middleware'e bağlı uç ve sayfa listesi, pozisyonsuz kullanıcı sayısı ve kapsamlı tabloya dokunan ham SQL sorguları. Yan etkisizdir. slugCollisions BOŞ DEĞİLSE dönüşüm yapılmamalıdır: kompleks model o adlarla sistem tabloları kurar.",
|
|
336
|
+
inputSchema: { projectId: guid },
|
|
337
|
+
annotations: readOnly
|
|
338
|
+
}, handle(config, async ({ projectId }) => {
|
|
339
|
+
const payload = await platform.org('POST', `/projects/${projectId}/access-model/preflight`);
|
|
340
|
+
const data = (payload?.data ?? {});
|
|
341
|
+
const environments = Array.isArray(data.environments) ? data.environments : [];
|
|
342
|
+
const collisions = environments.flatMap(entry => Array.isArray(entry.slugCollisions) ? entry.slugCollisions : []);
|
|
343
|
+
const withoutOwner = environments.reduce((total, entry) => total + (typeof entry.entitiesWithoutOwnerFields === 'number' ? entry.entitiesWithoutOwnerFields : 0), 0);
|
|
344
|
+
const withDirectRoles = environments.reduce((highest, entry) => Math.max(highest, typeof entry.usersWithDirectRoles === 'number' ? entry.usersWithDirectRoles : 0), 0);
|
|
345
|
+
const directRoleNote = withDirectRoles > 0
|
|
346
|
+
? ` AYRICA: bir ortamda ${withDirectRoles} kullanıcının doğrudan rol ataması (user_roles satırı) var. KOMPLEKS modelde doğrudan rol yetki VERMEZ — özne rolleri yalnız pozisyondan gelir (S-15). Sayı koltuğu olanları da kapsar; koltuğu OLMAYANLAR dönüşümden sonra her şeyden reddedilir, önce user_position_replace ile oturtun (kimin koltuksuz olduğu usersWithoutPositions ile çaprazlanır).`
|
|
347
|
+
: '';
|
|
348
|
+
return {
|
|
349
|
+
...payload,
|
|
350
|
+
note: (collisions.length > 0
|
|
351
|
+
? `Tablo adı çakışması var (${[...new Set(collisions)].join(', ')}): kompleks model bu adlarla kendi sistem tablolarını kurar, dönüşümden ÖNCE bu entity'ler yeniden adlandırılmalıdır.`
|
|
352
|
+
: withoutOwner > 0
|
|
353
|
+
? `${withoutOwner} entity'de sahiplik alanı yok; dönüşüm bunları arka planda ekler (owner-fields işi) ve satır kapsamları ancak o iş bittikten sonra doğru çalışır.`
|
|
354
|
+
: 'Ön denetimde engelleyici bulgu yok. Dönüşüm yine de ortam başına asenkron koşar — access_model_conversion_tasks ile izleyin.') + directRoleNote
|
|
355
|
+
};
|
|
356
|
+
}));
|
|
357
|
+
server.registerTool('access_model_convert', {
|
|
358
|
+
title: 'Erişim modelini dönüştür',
|
|
359
|
+
description: "Projeyi basit ↔ kompleks erişim modeline çevirir (POST /projects/{id}/access-model). GENİŞ ETKİLİ: kompleks'e geçişte organizasyon şeması uygulanır, kaynak kataloğu kurulur, geçiş politikası açılır ve sahiplik alanları arka planda doldurulur; basit'e dönüşte kompleks yetkilendirme kapanır. Her aktif environment için ayrı bir dönüşüm görevi açılır ve iş ASENKRON koşar — yanıt 'başladı' demektir, 'bitti' demez. Önce access_model_preflight, sonra access_model_conversion_tasks. enforcementMode 'enforce' olamaz: kompleks'e yeni geçen proje önce shadow'da izlenir.",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
projectId: guid,
|
|
362
|
+
target: z.enum(['simple', 'complex']).describe('Hedef model'),
|
|
363
|
+
enforcementMode: z.enum(['off', 'shadow']).optional().describe("Kompleks'e geçişte aktif environment'ların zorlama modu; varsayılan 'shadow'")
|
|
364
|
+
},
|
|
365
|
+
annotations: writes
|
|
366
|
+
}, handle(config, async ({ projectId, target, enforcementMode }) => {
|
|
367
|
+
const payload = await platform.org('POST', `/projects/${projectId}/access-model`, { body: { target, ...(enforcementMode === undefined ? {} : { enforcementMode }) } });
|
|
368
|
+
return { ...payload, note: 'Dönüşüm ortam başına asenkron koşuyor; durumu access_model_conversion_tasks ile izleyin — görev error ile kapanırsa o ortam DÖNÜŞMEMİŞ kalır.' };
|
|
369
|
+
}));
|
|
370
|
+
server.registerTool('access_model_conversion_tasks', {
|
|
371
|
+
title: 'Dönüşüm görevlerini listele',
|
|
372
|
+
description: "Projenin erişim modeli dönüşüm görevlerini listeler (GET /projects/{id}/access-model/tasks): environment, hedef model, durum (pending · processing · completed · error), deneme sayısı, hata metni ve başlangıç/bitiş zamanı. Dönüşümün gerçekten bittiğinin tek kanıtı budur; error kalan bir ortam dönüştürülmemiştir ve dönüşüm yeniden tetiklenmelidir.",
|
|
373
|
+
inputSchema: { projectId: guid },
|
|
374
|
+
annotations: readOnly
|
|
375
|
+
}, handle(config, async ({ projectId }) => {
|
|
376
|
+
const payload = await platform.org('GET', `/projects/${projectId}/access-model/tasks`);
|
|
377
|
+
const tasks = payload?.data ?? [];
|
|
378
|
+
const failed = tasks.filter(task => task.status === 'error');
|
|
379
|
+
const running = tasks.filter(task => task.status === 'pending' || task.status === 'processing');
|
|
380
|
+
return {
|
|
381
|
+
...payload,
|
|
382
|
+
summary: { total: tasks.length, running: running.length, failed: failed.length },
|
|
383
|
+
note: failed.length > 0
|
|
384
|
+
? `${failed.length} ortamın dönüşümü HATA ile kapandı (${failed.map(task => String(task.environment)).join(', ')}): o ortamlar dönüştürülmemiş durumdadır.`
|
|
385
|
+
: running.length > 0
|
|
386
|
+
? `${running.length} ortamın dönüşümü sürüyor; bitmeden kompleks yetkilendirme o ortamda eksik çalışır.`
|
|
387
|
+
: 'Bütün dönüşüm görevleri tamamlandı.'
|
|
388
|
+
};
|
|
389
|
+
}));
|
|
390
|
+
registerAccessOperationTools(server, platform);
|
|
391
|
+
registerOrganizationStructureTools(server, platform);
|
|
392
|
+
registerAccessPolicyTools(server, platform);
|
|
393
|
+
registerAccessRuntimeTools(server, platform);
|
|
27
394
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, readOnly, writes, updates } from './common.js';
|
|
2
|
+
import { accessResourceTypes } from '../knowledge/access.js';
|
|
3
|
+
import { PlatformApiError } from '../platform/errors.js';
|
|
4
|
+
import { handle } from '../platform/responses.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
const enforcementModes = ['off', 'shadow', 'enforce'];
|
|
7
|
+
const isoDate = z.string().min(1)
|
|
8
|
+
.describe("ISO 8601 zaman damgası (UTC), ör. '2026-09-01T00:00:00Z'");
|
|
9
|
+
function asRecord(value) {
|
|
10
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
11
|
+
}
|
|
12
|
+
export function registerAccessOperationTools(server, platform) {
|
|
13
|
+
const config = platform.config;
|
|
14
|
+
server.registerTool('access_matrix_get', {
|
|
15
|
+
title: 'Politika matrisini getir',
|
|
16
|
+
description: "Bir politikanın izin × kaynak matrisini getirir (GET /{project}/access/policies/{id}/matrix): actions sütunları (izin slug'ı + supportsScope/supportsFieldAccess), rows satırları (kaynak) ve her hücrede o politikadaki grant (grantId · effect · scope · fieldAccess · hasCondition). Politikayı grant grant okumaktan farkı: matris kaynak tipini bir tablo olarak gösterir, boş hücre 'grant yok' demektir. access_matrix_save'in şablonudur.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
project: projectParam,
|
|
19
|
+
environment: environmentParam,
|
|
20
|
+
id: guid.describe("Politika id'si (access_policy_list)"),
|
|
21
|
+
resourceType: z.enum(accessResourceTypes).describe('Matrisin kaynak tipi — zorunlu; her çağrı tek tipi tablolar'),
|
|
22
|
+
parentId: guid.optional().describe("Katalog satırının id'si (access_resource_list 'id'): o kaynağın çocuklarını tablolar — bir entity'nin alanları için entity katalog satırının id'si")
|
|
23
|
+
},
|
|
24
|
+
annotations: readOnly
|
|
25
|
+
}, handle(config, async ({ project, environment, id, resourceType, parentId }) => {
|
|
26
|
+
const payload = await platform.tenant('GET', project, `/access/policies/${id}/matrix`, { environment, query: { resourceType, ...(parentId ? { parentId } : {}) } });
|
|
27
|
+
const data = asRecord(payload?.data);
|
|
28
|
+
const rows = Array.isArray(data.rows) ? data.rows.map(asRecord) : [];
|
|
29
|
+
return {
|
|
30
|
+
...payload,
|
|
31
|
+
summary: {
|
|
32
|
+
policyId: data.policyId,
|
|
33
|
+
resourceType: data.resourceType,
|
|
34
|
+
actions: Array.isArray(data.actions) ? data.actions.length : 0,
|
|
35
|
+
rows: rows.length,
|
|
36
|
+
filledCells: rows.reduce((total, row) => total + (Array.isArray(row.cells) ? row.cells.map(asRecord).filter(cell => cell.grantId !== undefined && cell.grantId !== null).length : 0), 0)
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}));
|
|
40
|
+
server.registerTool('access_matrix_save', {
|
|
41
|
+
title: 'Politika matrisini kaydet',
|
|
42
|
+
description: "Matris üzerinden grant ekler/günceller/siler (PUT /{project}/access/policies/{id}/matrix). 'changes' verdiğiniz grant'leri yazar (id varsa günceller, yoksa açar), 'removedGrantIds' silinecek grant'lerin id'leridir. Politikanın ATAMALARINA dokunmaz — atama access_policy_update'in işidir. access_policy_update'ten farkı: politikanın grant listesinin tamamını göndermek gerekmez, yalnız değişen hücreler gider. Gövde platformun FluentValidation kısıtlarından geçer (deny grant'i ALAN ERİŞİMİ taşımaz; kapsam ve koşul taşıyabilir — o zaman eylemi kesmez, yalnız kapsamındaki satırları süzer, K-10/TB-94). Politikada SÜRESİ DOLMAMIŞ bir kullanıcı istisnası (`overridesDeny`; henüz başlamamış, ileri tarihli olanı da sayılır) varsa grant'lerine dokunmak — ekleme, silme ya da değiştirme — `access.override.manage` iznini ister; izinsiz istek 403 `AccessOverridePermissionRequiredException` döner ve hiçbir şey yazılmaz (ACC-009).",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
project: projectParam,
|
|
45
|
+
environment: environmentParam,
|
|
46
|
+
id: guid.describe("Politika id'si"),
|
|
47
|
+
resourceType: z.enum(accessResourceTypes).optional().describe('Yanıtta dönecek matrisin kaynak tipi'),
|
|
48
|
+
parentId: guid.optional().describe('Yanıtta dönecek matrisin katalog kökü'),
|
|
49
|
+
changes: z.array(z.record(z.unknown())).optional().describe("Yazılacak grant'ler (access_matrix_get hücre biçimi + permission/resourceKey); boş bırakılabilir"),
|
|
50
|
+
removedGrantIds: z.array(guid).optional().describe("Silinecek grant id'leri")
|
|
51
|
+
},
|
|
52
|
+
annotations: updates
|
|
53
|
+
}, handle(config, ({ project, environment, id, resourceType, parentId, changes, removedGrantIds }) => {
|
|
54
|
+
if ((changes ?? []).length === 0 && (removedGrantIds ?? []).length === 0) {
|
|
55
|
+
throw new PlatformApiError("access_matrix_save: ne 'changes' ne 'removedGrantIds' verildi — istek matriste hiçbir şeyi değiştirmezdi, gönderilmedi.", 400);
|
|
56
|
+
}
|
|
57
|
+
return platform.tenant('PUT', project, `/access/policies/${id}/matrix`, {
|
|
58
|
+
environment,
|
|
59
|
+
body: { changes: changes ?? [], removedGrantIds: removedGrantIds ?? [], ...(resourceType ? { resourceType } : {}), ...(parentId ? { parentId } : {}) }
|
|
60
|
+
});
|
|
61
|
+
}));
|
|
62
|
+
server.registerTool('access_resource_rebuild', {
|
|
63
|
+
title: 'Kaynak kataloğunu yeniden kur',
|
|
64
|
+
description: "Kaynak kataloğunu projenin güncel tanımlarından yeniden üretir (POST /{project}/access/resources/rebuild): yeni entity/alan/uç/sayfa katalogda yoksa ya da silinmiş kaynakların satırları (access_policy_list'te orphaned: true) kaldıysa bunu düzeltir. Grant'ler resourceKey ile bağlıdır, anahtar değişmediği sürece rebuild politikaları bozmaz. Katalog yalnız kompleks projede vardır.",
|
|
65
|
+
inputSchema: { project: projectParam, environment: environmentParam },
|
|
66
|
+
annotations: writes
|
|
67
|
+
}, handle(config, async ({ project, environment }) => {
|
|
68
|
+
const payload = await platform.tenant('POST', project, '/access/resources/rebuild', { environment });
|
|
69
|
+
return { ...payload, note: "Katalog yeniden kuruldu; grant'lerin bağlandığı anahtarları access_resource_list ile doğrulayın." };
|
|
70
|
+
}));
|
|
71
|
+
server.registerTool('access_decision_log_list', {
|
|
72
|
+
title: 'Erişim karar kayıtlarını listele',
|
|
73
|
+
description: "Karar kayıtlarını listeler (GET /{project}/access/decision-logs): zaman penceresi, karar, izin, kaynak anahtarı, neden, satır kapsamının metni, istek yolu ve korelasyon id'si. İki tür yalnız gölge modda yazılır — 'would-deny' (zorlama AÇIK OLSAYDI istek reddedilecekti) ve 'would-filter' (istek geçerdi ama satırları kapsam süzerdi); iki tür ise **enforce modda da** yazılır: 'unrestricted' (isUnrestricted sorgu koştu — TB-103) ve 'elevated' (runAs: system koştu — TB-105). enforce'a geçmeden önce okunacak liste budur; geçildikten sonra da yükseltme izi buradadır. Tek tek kayıtlar için bu, toplamlar için access_decision_log_summary, en çok engellenen kullanıcılar için access_decision_log_users.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
project: projectParam,
|
|
76
|
+
environment: environmentParam,
|
|
77
|
+
from: isoDate,
|
|
78
|
+
to: isoDate,
|
|
79
|
+
decision: z.enum(['would-deny', 'would-filter', 'would-mask', 'unrestricted', 'elevated']).optional().describe("Süzgeç; VERİLMEZSE tüm kararlar döner — ölçüldü 2026-09-19: aynı pencerede süzgeçsiz 10, 'would-filter' 7, 'would-deny' 3 kayıt. 'would-deny' = zorlama açık olsaydı istek reddedilirdi, 'would-filter' = istek geçerdi ama satırlar kapsamla süzülürdü (okuma yolunda filteredRowCount kaç satırın düşeceğini yazar), 'would-mask' = alan maskeli ya da gizli dönerdi (permission 'field.read', resourceKey 'field:<entityId>:<fieldId>', reason 'masked'|'hidden'), 'unrestricted' = isUnrestricted bir sorgu koştu (resourceKey entity anahtarı, reason entity slug'ı), 'elevated' = runAs: system bir akış koştu (resourceKey 'workflow:<id>', reason akış slug'ı, rowScopeText 'author:<akisi yazan kullanici>'). Son ikisi enforce modda da yazılır. Platform yalnız bu beşini yazar (AccessDecisionLogKind); tanımadığı bir değeri doğrulamaz, sessizce BOŞ liste döndürür"),
|
|
80
|
+
pageIndex: z.number().int().min(0).optional(),
|
|
81
|
+
pageLength: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200')
|
|
82
|
+
},
|
|
83
|
+
annotations: readOnly
|
|
84
|
+
}, handle(config, async ({ project, environment, ...query }) => {
|
|
85
|
+
const payload = await platform.tenant('GET', project, '/access/decision-logs', { environment, query });
|
|
86
|
+
const rows = Array.isArray(payload?.data) ? payload.data.map(asRecord) : [];
|
|
87
|
+
return {
|
|
88
|
+
...payload,
|
|
89
|
+
pageSummary: {
|
|
90
|
+
rows: rows.length,
|
|
91
|
+
withFilteredRowCount: rows.filter(row => typeof row.filteredRowCount === 'number').length,
|
|
92
|
+
wouldMask: rows.filter(row => row.decision === 'would-mask').length
|
|
93
|
+
},
|
|
94
|
+
note: "Sayımlar YALNIZ dönen sayfayı kapsar (pageSummary), pencerenin tamamını değil — pencere toplamı için access_decision_log_summary. filteredRowCount yalnız OKUMA yolundaki 'would-filter' kayıtlarında doludur (toplam − kapsamlı toplam) ve satırın PENCERE TOPLAMIDIR, istek başına değil: istek başına düşen satır sayısı için filteredRowCount / count (ölçüldü 2026-09-19: count 22, filteredRowCount 352 → istek başına 16). Güncelleme ve silme yolunda null kalır. 'would-mask' istek başına entity başına bir kez ÇÖZÜLÜR, ama maskeli/gizli HER ALAN için bir kayıt düşer ve yalnız sorgu satır döndürdüğünde yazılır."
|
|
95
|
+
};
|
|
96
|
+
}));
|
|
97
|
+
server.registerTool('access_decision_log_summary', {
|
|
98
|
+
title: 'Karar kayıtlarını özetle',
|
|
99
|
+
description: "Karar kayıtlarını izin × kaynak × neden kırılımında toplar (GET /{project}/access/decision-logs/summary). Gölge modda 'hangi izin nerede eksik' sorusunun cevabı budur: en çok tekrar eden satır, politikanın en belirgin boşluğudur. Tek tek kayıtlar için access_decision_log_list.",
|
|
100
|
+
inputSchema: { project: projectParam, environment: environmentParam, from: isoDate, to: isoDate },
|
|
101
|
+
annotations: readOnly
|
|
102
|
+
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/decision-logs/summary', { environment, query })));
|
|
103
|
+
server.registerTool('access_decision_log_users', {
|
|
104
|
+
title: 'Engellenen kullanıcıları listele',
|
|
105
|
+
description: "Karar kayıtlarını kullanıcı kırılımında toplar (GET /{project}/access/decision-logs/users): en çok 'reddedilirdi' kaydı üreten kullanıcılar. Bir kullanıcı listenin başındaysa çoğu zaman pozisyonu ya da atanan politikası eksiktir — access_preview ile o kullanıcının tek bir kararını açıklayın.",
|
|
106
|
+
inputSchema: { project: projectParam, environment: environmentParam, from: isoDate, to: isoDate, limit: z.number().int().min(1).max(1000).optional().describe('Platform varsayılanı 200') },
|
|
107
|
+
annotations: readOnly
|
|
108
|
+
}, handle(config, ({ project, environment, ...query }) => platform.tenant('GET', project, '/access/decision-logs/users', { environment, query })));
|
|
109
|
+
server.registerTool('access_enforcement_readiness', {
|
|
110
|
+
title: 'Zorlamaya hazır mı',
|
|
111
|
+
description: "Son N günün gölge kayıtlarında kaç 'reddedilirdi' olduğunu sayar ve sıfırsa hazır der (POST /{project}/access/enforcement/readiness). enforce'a geçmeden önceki kapı budur: isReady false ise geçiş bugün çalışan bir şeyi kesecek demektir — önce access_decision_log_summary ile boşlukları kapatın. Sayı sıfır olsa bile hiç trafik görmemiş bir uç kanıtlanmış olmaz.",
|
|
112
|
+
inputSchema: { project: projectParam, environment: environmentParam, days: z.number().int().min(1).max(30).optional().describe('Pencere (gün); varsayılan 7, en çok 30') },
|
|
113
|
+
annotations: readOnly
|
|
114
|
+
}, handle(config, async ({ project, environment, days }) => {
|
|
115
|
+
const payload = await platform.tenant('POST', project, '/access/enforcement/readiness', { environment, body: days === undefined ? {} : { days } });
|
|
116
|
+
const data = asRecord(payload?.data);
|
|
117
|
+
return {
|
|
118
|
+
...payload,
|
|
119
|
+
note: data.isReady === true
|
|
120
|
+
? "Pencerede 'reddedilirdi' kaydı yok. Bu, zorlamanın hiçbir şeyi kesmeyeceğini KANITLAMAZ — yalnız o pencerede denenen yolları kapsar; az kullanılan uçları access_preview ile ayrıca sınayın."
|
|
121
|
+
: `Pencerede ${String(data.wouldDenyCount ?? '?')} 'reddedilirdi' kaydı var: zorlama açılırsa bunlar kesilir. access_decision_log_summary ile izin/kaynak kırılımına bakın.`
|
|
122
|
+
};
|
|
123
|
+
}));
|
|
124
|
+
server.registerTool('access_enforcement_mode_set', {
|
|
125
|
+
title: 'Zorlama modunu değiştir',
|
|
126
|
+
description: "Bir environment'ın zorlama modunu değiştirir (PUT /environments/{projectId}/{environmentId}/enforcement-mode): 'off' karar motoru kapalı, 'shadow' kararlar hesaplanır ve reddedilecekler kaydedilir ama istek kesilmez, 'enforce' kararlar uygulanır. Yalnız kompleks projede anlamlıdır. 'enforce'a geçiş ÜRETİM DAVRANIŞINI DEĞİŞTİRİR: önce access_enforcement_readiness'ı yeşile getirin. Platform iki durumda confirm ister: production ortamında HER değişiklik ve HER ortamda 'off'a geçiş (409 EnforcementOffRequiresConfirmation) — 'off' satır kapsamını kaldırmakla kalmaz, korumalı alanları da herkese açık döndürür (TB-99).",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
projectId: guid,
|
|
129
|
+
environmentId: guid.describe('environment_list çıktısındaki id'),
|
|
130
|
+
enforcementMode: z.enum(enforcementModes),
|
|
131
|
+
confirm: z.boolean().optional().describe("Platformun istediği onay: production'da her değişiklik, her ortamda 'off'a geçiş. Bilinçli geçişte true")
|
|
132
|
+
},
|
|
133
|
+
annotations: updates
|
|
134
|
+
}, handle(config, ({ projectId, environmentId, enforcementMode, confirm }) => platform.org('PUT', `/environments/${projectId}/${environmentId}/enforcement-mode`, { body: { enforcementMode, confirm: confirm === true } })));
|
|
135
|
+
}
|