systa-mcp 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +23 -2
  2. package/SKILL.md +290 -55
  3. package/package.json +1 -1
  4. package/server.js +143 -16
package/README.md CHANGED
@@ -101,14 +101,14 @@ claude mcp add systa \
101
101
 
102
102
  ## Araçlar (tools)
103
103
 
104
- Toplam **64 araç** vardır; `tools/list` yanıtı anahtarın kapsamına göre filtrelenir
104
+ Toplam **79 araç** vardır; `tools/list` yanıtı anahtarın kapsamına göre filtrelenir
105
105
  (bkz. "Scope-aware araç listesi"). Aşağıdakiler en sık kullanılan çekirdek araçlardır —
106
106
  tam katalog `list_capabilities` ile keşfedilir.
107
107
 
108
108
  | Araç | Açıklama |
109
109
  | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
110
110
  | `list_requests` | Talepleri listele (limit/offset/search/statusId) — `request.read` |
111
- | `get_request` | Tek bir talebi id ile getir — `request.read` |
111
+ | `get_request` | Tek bir talebi talep numarasıyla getir — `request.tabs.general.view` |
112
112
  | `list_projects` | Projeleri listele — `project.read` |
113
113
  | `create_request` | Talep oluştur (title/description/companyId/statusId/assignedTo) — `request.create` |
114
114
  | `add_request_comment` | Talebe yorum ekle (düz metin otomatik TipTap'e sarılır) — `request.comment.create` + `request.tabs.comments.view` |
@@ -123,6 +123,27 @@ tam katalog `list_capabilities` ile keşfedilir.
123
123
  (örn. `POST /requests`, `POST /requests/42/comments`). Anahtarın scope'u dışındaki
124
124
  çağrılar `403` döner.
125
125
 
126
+ ### Alt form hazırlama
127
+
128
+ Alt form (`sub-forms`), ana talep formu (`form`) ile aynı modül değildir. Ajan önce
129
+ `list_capabilities({module:"sub-forms"})`, ardından kullanacağı uçlar için
130
+ `describe_endpoint` çağırmalıdır. Önerilen taslak akışı:
131
+
132
+ 1. `POST /sub-forms` ile `isPublic:false` bir form kabuğu oluşturun. `parentFormId`
133
+ backend tarafından şirketin aktif talep formundan çözülür; istemci göndermemelidir.
134
+ 2. Bölüm, alan ve kuralları ayrı ayrı yazmak yerine
135
+ `PUT /sub-forms/:id/save` ile tek transaction'da kaydedin. Yeni öğelerde `tempId`,
136
+ alan-bölüm ilişkisinde `sectionTempId` kullanılır; yanıt gerçek id haritalarını verir.
137
+ 3. Kullanım biçimini yalnız `embed` veya `request_attach` binding'iyle tanımlayın.
138
+ 4. `GET /sub-forms/:id/preview` HTML değil `{form, sections, fields, preview:true}`
139
+ JSON payload'ı döndürür. Query/rule/title/correlation test uçları kalıcı ayar yazmaz.
140
+ 5. Taslak özetini kullanıcıya göstermeden ve açık onay almadan publish/public erişim,
141
+ allowed origin, webhook, periodic veya correlation özelliklerini etkinleştirmeyin.
142
+
143
+ Tam alan türleri, atomik save gövdesi, binding kararları ve hata kurtarma akışı paketle
144
+ birlikte kurulan `SKILL.md` içindedir. Sürüm yükselttikten sonra skill'i yenilemek için
145
+ `npx -y systa-mcp@latest --install-skill` komutunu yeniden çalıştırın.
146
+
126
147
  ### Dosya ekleri (download_request_file / upload_file_to)
127
148
 
128
149
  `systa_api_call` JSON-only olduğundan binary taşıyamaz — dosya ekleri için bu iki
package/SKILL.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 1.2.0
2
+ version: 1.4.0
3
3
  name: systa
4
4
  description: |
5
5
  Drive SysTa (Talep Takip Sistemi / SysTa) — a
@@ -15,12 +15,14 @@ description: |
15
15
  "bana/şuna ata", "yorum ekle", "durumu değiştir /
16
16
  tamamla / kapat", "üzerimdeki işler", "proje aç",
17
17
  "kanban kartı ekle", "onaya sun", "onayla/reddet",
18
- "izin talebi", "rapor al / dışa aktar", "sürüm
19
- oluştur". Use when (EN): "create a SysTa request",
18
+ "izin talebi", "alt form hazırla / oluştur", "rapor
19
+ al / dışa aktar", "sürüm oluştur". Use when (EN):
20
+ "create a SysTa request",
20
21
  "log a ticket", "assign / reassign", "add a comment",
21
22
  "move/close the ticket", "show my open work", "create
22
23
  project", "add task card", "request approval",
23
- "approve/reject", "export report", "create release".
24
+ "approve/reject", "design/create a sub-form", "export
25
+ report", "create release".
24
26
  Multi-tenant (vendor schema) aware; resolves names →
25
27
  ids; honors dynamic required fields. NOT for: editing
26
28
  SysTa source code, running psql/DB migrations directly,
@@ -67,6 +69,8 @@ Before any SysTa action, confirm connectivity, auth and identity. Do this once p
67
69
 
68
70
  If bootstrap fails, deliver a one-line reason ("anahtar süresi dolmuş", "key expired", "endpoint için yetki yok") and stop — never fabricate a result.
69
71
 
72
+ 5. **Yorum yazacaksan ÖNCE sözleşmeyi oku.** Yorum gövdesi düz metin değil **TipTap JSON**'dur ve tablo/başlık/liste destekler; talep (`#125`) ve görev kartı (`##1809`) referansları özel işaret istemez. Yorum, durum notu veya talep açıklaması yazmadan önce → [Yorum ekle (comment)](#yorum-ekle-comment--add_request_comment--post-apirequestsrequestnumbercomments). Bu adım atlanırsa tablolar boş hücreyle, referanslar düz metin olarak çıkar.
73
+
70
74
  ## SysTa nedir / temel kavramlar (glossary)
71
75
 
72
76
  Short shared vocabulary. Use these words with the user; never expose raw REST paths or internal jargon.
@@ -85,6 +89,7 @@ Short shared vocabulary. Use these words with the user; never expose raw REST pa
85
89
  | SLA | SLA | Süre taahhütleri (ilk yanıt, çözüm). Instance bir talebe bağlanır; pause/resume edilebilir. |
86
90
  | Onay | Approval | Toplu onaya gönderme + onayla/reddet; plan onayı ve talep onayı ayrı akışlar. |
87
91
  | Sürüm | Release | Yazılım sürüm yönetimi: liste → item → onay → Go/No-Go → deployment workflow. |
92
+ | Alt form | Sub-form | Talep formuna gömülen veya talep akışına bağlanan, bölüm/alan/kural/binding içeren ek veri toplama şeması. Ana istek formu (`form`) ile aynı şey değildir. |
88
93
  | Vendor şema | Vendor schema | Multi-tenant izolasyon; her vendor ayrı schema (`vendor_xxx`). Public referans alınmaz. |
89
94
 
90
95
  ### Anahtar kelime → modül eşlemesi (keyword index)
@@ -94,54 +99,58 @@ yanıtındaki `glossary` alias'larının ayna kopyasıdır (MCP yokken de geçer
94
99
  ASCII'dir — eşleştirmede aksanı yok say. Modülü bulduktan sonra `describe_module(modül)`
95
100
  veya `describe_endpoint` ile gövdeyi netleştir.
96
101
 
97
- | Doğal dil (alias) | Modül | Birincil giriş |
98
- | ------------------------------------------------------------ | --------------- | ----------------------------------------------------------------- |
99
- | talep, talepler, is, destek talebi | request | list_requests / create_request |
100
- | geciken, overdue, gecikmis, sla ihlali, deadline gecmis | request/sla | list_requests(overdue:true / slaBreached:true) |
101
- | toplu guncelle, hepsini, birden cok talep | request | bulk_update_requests |
102
- | kullanici bul, isim cozumle, kime, hangi ahmet | user | resolve_user_by_name / list_users |
103
- | sirket bul, kategori bul, ekip bul, id cozumle, lookup | multiple | list_lookup(kind:'company'/'category'/'team'/...) |
104
- | projeyi klonla, projeyi cogalt, duplicate project | project | duplicate_project |
105
- | snapshot fark, baseline karsilastir, plan diff | plan | compare_plan_baseline |
106
- | yorumlar, ekler, degisiklik gecmisi, denetim | request | list_request_comments / list_request_files / list_request_history |
107
- | gorev, task, kart, card | kanban | manage_request_card / move_request_card |
108
- | pano, board, kanban / kolon, column, asama, liste | kanban | get_request_board / create_request_board |
109
- | durum gecisi onizle, izinli gecisler, hangi duruma gecebilir | request | list_allowed_transitions |
110
- | plan modu, sprint kapsami, plan mode setup | plan | create_plan_mode |
111
- | durum, oncelik, atanan, planla, denetim, gecmis | request | get_request / list_requests |
112
- | tahmini efor, planlanan sure (estimatedEffort) | request | update_request (TAHMIN alani, defter DEGIL) |
113
- | efor gir, efor yaz, calistigim sure, efor defteri, mesai | effort | log_effort / list_effort_targets / list_effort_entries |
114
- | eksik efor girisim var mi, efor uyumu | effort | get_effort_compliance |
115
- | proje, klonla | project | list_projects / create_project |
116
- | onaya sunma, snapshot, sprint, sprinti kapat, optimize et | plan | submit_plan_for_approval / create_plan_baseline |
117
- | onay, onayla, reddet | approvals | list_pending_approvals / respond_to_approval |
118
- | onay tipleri, kime onaya gonderelim | approvals | list_approval_types |
119
- | red nedeni, neden reddedildi, gecerli red sebepleri | approvals | list_rejection_reasons |
120
- | bu talep neden dondu, neden yazamiyorum, kilitli alan | approvals | get_request_freeze |
121
- | sla, sure taahhudu | sla | (sla endpoints) |
122
- | release, surum, go/no-go, deployment | release | create_release_list / add_release_item |
123
- | musaitlik, izin (leave), vekil | user/automation | systa_api_call /user-availability, /user-leaves |
124
- | vendor, sirket, musteri | company | systa_api_call GET /companies |
125
- | organizasyon birimi, departman, bolum, birim | department | GET /api/departments |
126
- | takim, ekip, team | team | GET /api/teams |
127
- | yetki paketi, izin (permission) | security | (bundle/permission endpoints) |
128
- | alt form, sub-form | forms | systa_api_call GET /forms/:id |
129
- | rapor, raporlama, disa aktar, export, excel, csv | report | systa_api_call /reports/preview,/reports/export |
130
- | ozel sorgu, sorgu, query builder, ad-hoc, veri cek | query-builder | querybuilder_describe_fields / \_execute_query |
131
- | hesap tablosu, gridpad, tablo, excel, spreadsheet, hucre | spreadsheet | GET /api/spreadsheets |
132
- | dosya, ek, attachment, yukle, upload, indir, download | file | upload_file_to / download_request_file |
133
- | bildirim, notification, uyari, okundu, push | notification | GET /api/notifications |
134
- | yetenek, beceri, skill, otomatik etiketleme | skill | GET /api/skills |
135
- | duyuru, announcement, bildiri, ilan | announcement | GET /api/announcements |
136
- | urun guncellemesi, surum notu, yenilikler, changelog | product-update | GET /api/product-updates |
137
- | not, kisisel not, hatirlatma | note | GET /api/dashboard/notes |
138
- | yer imi, favori, bookmark, kisayol | bookmark | GET /api/dashboard/bookmarks |
139
- | degerlendirme, puanlama, memnuniyet, geri bildirim | ratings | POST /api/ratings |
140
- | anket, oylama, karar alalim, ekibe soralim, secenek sun | decisions | systa_api_call POST /requests/:n/decisions |
141
- | github, repo, pull request, commit, issue aktar | github | systa_api_call /github/me/{connection,repos,import,push} |
142
- | gosterge paneli, dashboard, widget, ozet, grafik, panel | dashboard | GET /api/dashboard/init |
143
-
144
- > Bu tablo, katalogdaki ~25 modülün kullanıcı-yüzeyli olanlarından bir alt küme kapsar. Listede olmayan
102
+ | Doğal dil (alias) | Modül | Birincil giriş |
103
+ | ------------------------------------------------------------ | ----------------- | ----------------------------------------------------------------------------------------------- |
104
+ | talep, talepler, is, destek talebi | request | list_requests / create_request |
105
+ | geciken, overdue, gecikmis, sla ihlali, deadline gecmis | request/sla | list_requests(overdue:true / slaBreached:true) |
106
+ | toplu guncelle, hepsini, birden cok talep | request | bulk_update_requests |
107
+ | kullanici bul, isim cozumle, kime, hangi ahmet | user | resolve_user_by_name / list_users |
108
+ | sirket bul, kategori bul, ekip bul, id cozumle, lookup | multiple | list_lookup(kind:'company'/'category'/'team'/...) |
109
+ | projeyi klonla, projeyi cogalt, duplicate project | project | duplicate_project |
110
+ | snapshot fark, baseline karsilastir, plan diff | plan | compare_plan_baseline |
111
+ | yorumlar, ekler, degisiklik gecmisi, denetim | request | list_request_comments / list_request_files / list_request_history |
112
+ | yorum yaz, tablo, referans, #125, ##1809, kod blogu, baslik | request | add_request_comment — govde TipTap JSON; tablo/liste commentTextJson ile, referanslar DUZ METIN |
113
+ | gorev, task, kart, card | kanban | manage_request_card / move_request_card |
114
+ | pano, board, kanban / kolon, column, asama, liste | kanban | get_request_board / create_request_board |
115
+ | durum gecisi onizle, izinli gecisler, hangi duruma gecebilir | request | list_allowed_transitions |
116
+ | plan modu, sprint kapsami, plan mode setup | plan | create_plan_mode |
117
+ | durum, oncelik, atanan, planla, denetim, gecmis | request | get_request / list_requests |
118
+ | tahmini efor, planlanan sure (estimatedEffort) | request | update_request (TAHMIN alani, defter DEGIL) |
119
+ | efor gir, efor yaz, calistigim sure, efor defteri, mesai | effort | log_effort / list_effort_targets / list_effort_entries |
120
+ | eksik efor girisim var mi, efor uyumu | effort | get_effort_compliance |
121
+ | proje, klonla | project | list_projects / create_project |
122
+ | onaya sunma, snapshot, sprint, sprinti kapat, optimize et | plan | submit_plan_for_approval / create_plan_baseline |
123
+ | onay, onayla, reddet | approvals | list_pending_approvals / respond_to_approval |
124
+ | onay tipleri, kime onaya gonderelim | approvals | list_approval_types |
125
+ | red nedeni, neden reddedildi, gecerli red sebepleri | approvals | list_rejection_reasons |
126
+ | bu talep neden dondu, neden yazamiyorum, kilitli alan | approvals | get_request_freeze |
127
+ | sla, sure taahhudu | sla | (sla endpoints) |
128
+ | release, surum, go/no-go, deployment | release | create_release_list / add_release_item (listeyi ID ile hedefle) |
129
+ | surum numarasi, versionLabel, ayni gun iki liste | release | create_release_list -> id + versionLabel (tarih DEGIL) |
130
+ | musaitlik, uygunluk | user-availability | systa_api_call GET /user-availability |
131
+ | izin (leave), izin hakkı, takım izin takvimi | leave-management | systa_api_call /leave-management, /user-leaves |
132
+ | vekil, delegate, proxy | automation-rules | systa_api_call /automation-rules |
133
+ | vendor, sirket, musteri | company | systa_api_call GET /companies |
134
+ | organizasyon birimi, departman, bolum, birim | company | GET /api/departments |
135
+ | takim, ekip, team | team | GET /api/teams |
136
+ | yetki paketi, izin (permission) | security | (bundle/permission endpoints) |
137
+ | alt form, sub-form, subform, ek form, mini form | sub-forms | systa_api_call GET /sub-forms/:id |
138
+ | rapor, raporlama, disa aktar, export, excel, csv | report | systa_api_call /reports/preview,/reports/export |
139
+ | ozel sorgu, sorgu, query builder, ad-hoc, veri cek | query-builder | querybuilder_describe_fields / \_execute_query |
140
+ | hesap tablosu, gridpad, tablo, excel, gridPad, hucre | gridpad | GET /api/gridpads |
141
+ | dosya, ek, attachment, yukle, upload, indir, download | file | upload_file_to / download_request_file |
142
+ | bildirim, notification, uyari, okundu, push | notification | GET /api/notifications |
143
+ | yetenek, beceri, skill, otomatik etiketleme | skills | GET /api/skills |
144
+ | duyuru, announcement, bildiri, ilan | announcements | GET /api/announcements |
145
+ | urun guncellemesi, surum notu, yenilikler, changelog | product-updates | GET /api/product-updates |
146
+ | not, kisisel not, hatirlatma | dashboard | GET /api/dashboard/notes |
147
+ | yer imi, favori, bookmark, kisayol | dashboard | GET /api/dashboard/bookmarks |
148
+ | degerlendirme, puanlama, memnuniyet, geri bildirim | ratings | POST /api/ratings |
149
+ | anket, oylama, karar alalim, ekibe soralim, secenek sun | decisions | systa_api_call POST /requests/:n/decisions |
150
+ | github, repo, pull request, commit, issue aktar | github | systa_api_call /github/me/{connection,repos,import,push} |
151
+ | gosterge paneli, dashboard, widget, ozet, grafik, panel | dashboard | GET /api/dashboard/init |
152
+
153
+ > Bu tablo, dinamik katalogdaki kullanıcı-yüzeyli modüllerin bir alt kümesini kapsar. Listede olmayan
145
154
  > bir kelime için `list_capabilities` çağır — anahtarının erişebildiği güncel modül/alias
146
155
  > kümesini döndürür (kapsam-filtreli; yetkisiz modül görünmez).
147
156
 
@@ -151,7 +160,7 @@ veya `describe_endpoint` ile gövdeyi netleştir.
151
160
  2. **No jargon.** Don't narrate "calling describe_endpoint", "POST /api/requests", "resolving statusId". Speak in the product's user verbs.
152
161
  3. **Detect language and reply in it.** If the user writes Turkish, answer in Turkish; if English, English. Field keys and enum values passed to the API stay as the API expects (`statusKey`, `priority=high`) — only the conversation is localized.
153
162
  4. **Smart defaults + ask one thing at a time.** Apply opinionated defaults from the intent summary (`channel='API'`, `priority='normal'`, assignee=self for "bana", `strategy='any_one'`/`'all_required'` per module). Ask **one** genuinely-missing thing, never a batch ("ver bana başlık, şirket, durum, öncelik, kategori" YASAK). Never invent a `title`, a password, a webhook URL, or a secret — ask for those.
154
- 5. **Resolve dynamic required fields up front.** Before a create/mutation, resolve the entity's required fields _for this vendor_ (e.g. `statusId` via `GET /api/statuses`, `companyId` from `who-am-i`, approver name → `userId` via user lookup). Don't submit a half-filled body and let it 400 — pre-resolve, then submit. Forms with dynamic schemas: pull the schema first (via `systa_api_call`, e.g. `GET /forms/:id`) so required fields are known.
163
+ 5. **Resolve dynamic required fields up front.** Before a create/mutation, resolve the entity's required fields _for this vendor_ (e.g. `statusId` via `GET /api/statuses`, `companyId` from `who-am-i`, approver name → `userId` via user lookup). Don't submit a half-filled body and let it 400 — pre-resolve, then submit. For a main request form use the `form` module; for an alt form use `describe_endpoint('/api/sub-forms/:id','GET')` and then read `GET /api/sub-forms/:id`. Never substitute `/forms/:id` for an alt form.
155
164
  6. **Safety tiers gate confirmation.** `read_only` → just do it. `mutating`/`idempotent` → do it, then report. **`destructive`** (revoke access, delete, hard removal) and live side-effecting actions (publish, send invite, advance release workflow, test webhook) → show a one-line "şunu yapacağım" and get an explicit OK first. Publishing (announcement/release notes/product update) is **never** auto-published — show a summary, get approval.
156
165
  7. **Don't over-ask scope.** `companyId`/`userId`/`vendorSchema` come from `who-am-i`; don't ask the user for them when a single sane value exists.
157
166
 
@@ -193,7 +202,98 @@ Each maps a P0 user verb to a tool/endpoint, with the opinionated defaults from
193
202
 
194
203
  ### Yorum ekle (comment) — `add_request_comment` → `POST /api/requests/:requestNumber/comments`
195
204
 
196
- - Plain text `commentText`. At least one of `commentText` / `commentTextJson` / `fileIds` required. No mentions unless asked.
205
+ > **Yorum gövdesi düz metin DEĞİL, TipTap JSON dokümanıdır.** Bu bölümü yorum yazmadan önce oku tablo, başlık, liste ve talep/görev referansları buradan çıkar.
206
+
207
+ **İki çağrı biçimi** (en az biri zorunlu: `text` / `commentTextJson` / `fileIds`):
208
+
209
+ | Ne yazıyorsun | Argüman | Not |
210
+ | ---------------------------------- | ----------------- | ------------------------------------------------------------------------ |
211
+ | Sıradan yorum | `text` | Tool tek paragrafa sarar |
212
+ | Tablo / başlık / liste / kod bloğu | `commentTextJson` | TAM doküman; verilirse `text` yok sayılır |
213
+ | Dosya eki | `fileIds` | Önce `upload_file_to`; dosya hem yorumda hem Dosyalar sekmesinde görünür |
214
+
215
+ **Render edilen düğümler** (otorite: frontend `jsonToHtml`; listede olmayan düğüm sessizce yalnız içeriğiyle basılır):
216
+ `doc`, `paragraph`, `heading{level:1..3}`, `text`, `hardBreak`, `bulletList`, `orderedList`, `listItem`, `blockquote`, `codeBlock{language}`, `table`, `tableRow`, `tableHeader`, `tableCell`, `mention{id,label}`.
217
+ **Metin işaretleri (marks):** `bold`, `italic`, `underline`, `strike`, `code`, `link{href}`, `textStyle{color,fontSize,fontFamily}`, `highlight{color}`.
218
+
219
+ **Tablo iç içe sırası KATIDIR** — `table > tableRow > (tableHeader | tableCell) > paragraph > text`.
220
+ Hücreye doğrudan `text` koyarsan hücre **boş** görünür.
221
+
222
+ ```json
223
+ {
224
+ "type": "doc",
225
+ "content": [
226
+ {
227
+ "type": "heading",
228
+ "attrs": { "level": 3 },
229
+ "content": [{ "type": "text", "text": "Bulgular" }]
230
+ },
231
+ {
232
+ "type": "table",
233
+ "content": [
234
+ {
235
+ "type": "tableRow",
236
+ "content": [
237
+ {
238
+ "type": "tableHeader",
239
+ "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Bulgu" }] }]
240
+ },
241
+ {
242
+ "type": "tableHeader",
243
+ "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Kart" }] }]
244
+ }
245
+ ]
246
+ },
247
+ {
248
+ "type": "tableRow",
249
+ "content": [
250
+ {
251
+ "type": "tableCell",
252
+ "content": [
253
+ {
254
+ "type": "paragraph",
255
+ "content": [{ "type": "text", "text": "Filo uyarısı çıkmıyor" }]
256
+ }
257
+ ]
258
+ },
259
+ {
260
+ "type": "tableCell",
261
+ "content": [
262
+ { "type": "paragraph", "content": [{ "type": "text", "text": "##1809" }] }
263
+ ]
264
+ }
265
+ ]
266
+ }
267
+ ]
268
+ },
269
+ {
270
+ "type": "paragraph",
271
+ "content": [
272
+ { "type": "text", "text": "Ayrıntı " },
273
+ { "type": "text", "marks": [{ "type": "bold" }], "text": "#125" },
274
+ { "type": "text", "text": " talebinde." }
275
+ ]
276
+ }
277
+ ]
278
+ }
279
+ ```
280
+
281
+ #### Talep ve görev kartı referansı — özel işaret GEREKMEZ
282
+
283
+ Gövdede geçen şu düz metin tokenları otomatik olarak **tıklanabilir rozete** çevrilir:
284
+
285
+ | Yazım | Neye gider | Tıklayınca |
286
+ | -------- | --------------------------- | ---------------------------------------------- |
287
+ | `#125` | **Talep** (request) | Talep önizlemesi açılır |
288
+ | `##1809` | **Görev kartı** (task card) | Kartın önizlemesi açılır, oradan karta gidilir |
289
+
290
+ - **Link mark'ı / `href` / öznitelik EKLEME** — renderer token'ı kendisi rozete çevirir. Elle `<a>` üretmek rozeti bozar.
291
+ - Sayı: `#` için **talep numarası**, `##` için kartın **global `card.id`**'si.
292
+ Panoda kartın üstünde görünen `#3` talep-içi **sıra numarasıdır**, referans DEĞİLDİR — `card.id`'yi `get_request_board` / `cards/list` çıktısından al.
293
+ - **Öneki karıştırmak kullanıcıyı yanlış kayda götürür**: aynı sayı hem talep hem kart olarak var olabilir. Kart kastediyorsan çift diyez şart.
294
+ - Rozetler her yorum görünümünde ve sürüm karar akışındaki yorumlarda da çalışır.
295
+
296
+ İstenmedikçe `@mention` ekleme.
197
297
 
198
298
  ### Durum değiştir / tamamla / kapat (change status) — `change_request_status` → `PUT /api/requests/:requestNumber`
199
299
 
@@ -231,6 +331,136 @@ Each maps a P0 user verb to a tool/endpoint, with the opinionated defaults from
231
331
 
232
332
  - No dedicated tool — drive via `systa_api_call`. `userId`=self (sormadan), `leaveType='annual'`, `isHalfDay=false`, dates normalized to `YYYY-MM-DD` ("yarın/cuma" → tarih), `returnToWorkDate` = next working day after end. "izinlerim" → `systa_api_call` `GET /user-leaves/me`. Needs the `user.leave.self` scope.
233
333
 
334
+ ### Alt form hazırla / oluştur (design a sub-form) — `systa_api_call` + `sub-forms`
335
+
336
+ Alt form, ana talep formu değildir. `form` modülü ana talep şemasını; `sub-forms`
337
+ modülü ise bağımsız/gömülü ek veri toplama şemasını yönetir. Alt form; form meta
338
+ bilgisi, bölümler, alanlar, kurallar ve nerede çalışacağını belirleyen binding'lerden
339
+ oluşur. Dedicated create aracı yoktur; keşif ve tüm çağrılar `systa_api_call` ile yapılır.
340
+
341
+ #### 1. İhtiyacı kısa bir forma çevir
342
+
343
+ Kullanıcıdan doğal dilde şu kararları çıkar; yalnız gerçekten eksik olan **tek** kararı sor:
344
+
345
+ - Amaç ve görünen ad. `formKey`, `sectionKey`, `fieldKey` değerlerini ajan üretir:
346
+ küçük harf + rakam + alt çizgi, en az 2 karakter (`musteri_geri_bildirim` gibi).
347
+ - Kullanım biçimi: doğrudan/gömülü veri toplayıp talep açma → formMode
348
+ 'create_request' + bindingType 'embed'; mevcut talep içinde görev/ek iş formu →
349
+ formMode 'create_task' + bindingType 'request_attach'; ikisi de gerekiyorsa formMode
350
+ 'both'. Binding türü yalnız 'embed' veya 'request_attach' olabilir.
351
+ - Bölümler ve alanlar. Alan türleri:
352
+ `text`, `textarea`, `number`, `currency`, `email`, `phone`, `date`, `datetime`,
353
+ `select`, `multiselect`, `checkbox`, `radio`, `file`, `hidden`, `custom_regex`,
354
+ `image`, `signature`, `location`, `rating`, `switch`, `richtext`.
355
+ Veri türü `string|number|boolean|date|file|json`; seçenekli alanlarda `optionsConfig`,
356
+ zorunlu alanlarda `isRequired:true`, iki kolon düzeninde `colSpan:1|2` kullan.
357
+ - Otomasyon, dış erişim veya yayın isteği açıkça söylenmediyse kural, webhook,
358
+ periyodik çalışma, public erişim, captcha secret ya da allowed origin ekleme.
359
+ Güvenli taslak varsayılanı `isPublic:false` ve yayında-değil durumudur.
360
+
361
+ Etiket/başlık gibi çevrilebilir alanları katalog şemasının istediği i18n nesnesiyle
362
+ gönder (örn. `{tr:'E-posta',en:'Email'}`). Kullanıcının verdiği mevcut id'leri başka
363
+ vendor'da yeniden kullanma; company/team/user/status/category/board/formRule adlarını
364
+ önce bu vendor içinde çöz.
365
+
366
+ #### 2. Uçları ve güncel şemayı keşfet
367
+
368
+ 1. `list_capabilities({module:'sub-forms'})` çağır. Ayrıca binding ve gönderi işi
369
+ varsa `describe_module('sub-form-bindings')` / `describe_module('sub-form-submissions')`
370
+ kullan. Modül görünmüyorsa kapsam dışıdır; endpoint uydurma.
371
+ 2. En az şu uçları `describe_endpoint(path,method)` ile oku:
372
+ `POST /api/sub-forms`, `PUT /api/sub-forms/:id/save`, `GET /api/sub-forms/:id/preview`
373
+ ve seçilen binding gerekiyorsa `POST /api/sub-forms/:id/bindings`.
374
+ 3. Mevcut bir form değişiyorsa önce `GET /api/sub-forms/:id` oku; kullanıcı açıkça
375
+ kaldır demedikçe mevcut section/field/rule kimliklerini koru.
376
+
377
+ #### 3. Önce taslak şemayı oluştur, sonra atomik kaydet
378
+
379
+ 1. `POST /api/sub-forms` ile yalnız temel formu oluştur: `formName`, üretilmiş
380
+ `formKey`, amaçtan seçilen `formMode`, `isPublic:false`. Kullanım biçimi belliyse
381
+ `initialBindingType` da gönderilebilir; 'request_attach' binding'de `formRuleId`
382
+ henüz yoksa binding pasif oluşur ve daha sonra tamamlanır. `parentFormId` gönderme:
383
+ backend bunu şirketin aktif ana talep formundan server-authoritative çözer ve
384
+ create/update/save sırasında istemci değerini yok sayar.
385
+ 2. Dönen alt form `id`'sini al. Bölüm/alan/kuralı tek tek yazmak yerine
386
+ `PUT /api/sub-forms/:id/save` kullan; bu işlem tek transaction'da kaydeder ve
387
+ form sürümünü artırır. Gövde:
388
+
389
+ ```json
390
+ {
391
+ "form": { "formName": "Müşteri Geri Bildirimi", "formMode": "create_request" },
392
+ "sections": [
393
+ {
394
+ "tempId": "sec_contact",
395
+ "sectionKey": "iletisim",
396
+ "title": { "tr": "İletişim" },
397
+ "displayOrder": 0
398
+ }
399
+ ],
400
+ "fields": [
401
+ {
402
+ "tempId": "fld_email",
403
+ "sectionTempId": "sec_contact",
404
+ "fieldKey": "email",
405
+ "fieldType": "email",
406
+ "dataType": "string",
407
+ "label": { "tr": "E-posta" },
408
+ "isRequired": true,
409
+ "displayOrder": 0,
410
+ "colSpan": 2
411
+ }
412
+ ],
413
+ "rules": [],
414
+ "deletedSectionIds": [],
415
+ "deletedFieldIds": [],
416
+ "deletedRuleIds": []
417
+ }
418
+ ```
419
+
420
+ Yeni bileşenlerde `tempId`, alan-bölüm ilişkisinde `sectionTempId` kullan. Yanıttaki
421
+ `sectionIdMap`, `fieldIdMap`, `ruleIdMap` gerçek id'leri verir. Mevcut bileşende
422
+ `id` gönder. `deleted*Ids` listelerine yalnız kullanıcının açıkça kaldırdığı mevcut
423
+ id'leri koy; listede olmayan öğeleri “eksik geldi” diye silme.
424
+
425
+ 3. İlk create'te binding kurulmadıysa `POST /api/sub-forms/:id/bindings` çağır.
426
+ 'embed' site/portal içinde doğrudan çalışır. 'request_attach' mevcut talep akışına
427
+ `formRuleId` ile bağlanır; rule çözülene kadar pasif kalabilir. Rastgele talep/proje
428
+ id'sini binding diye gönderme.
429
+
430
+ #### 4. Doğrula; kullanıcı onayı olmadan yayınlama
431
+
432
+ - `GET /api/sub-forms/:id` ile kaydedilmiş tanımı, ardından
433
+ `GET /api/sub-forms/:id/preview` ile `{form,sections,fields,preview:true}` önizleme
434
+ payload'ını oku. Bu uç HTML/render edilmiş ekran döndürmez; görünümü istemci çizer.
435
+ - Dinamik query alanında `POST /api/sub-forms/:id/fields/:fieldId/test-query` çağrısı
436
+ yapılandırmayı kaydetmez ama tanımlı prefix-set/regex veya sandboxed read-only SQL'i
437
+ gerçekten çalıştırır. Yerleşik doğrulama ve lookup setlerini önce
438
+ `GET /api/sub-forms/query/prefix-sets` ile keşfet. Kurallar için
439
+ `POST /api/sub-forms/:id/rules/test` side-effect üretmeyen dry-run'dır.
440
+ - Kullanıcıya ad, kullanım biçimi, bölüm/alan listesi, zorunlu alanlar, binding ve
441
+ varsa kuralları tek özetle göster. `PATCH /api/sub-forms/:id/publish` yalnız açık
442
+ “yayınla/devreye al” onayından sonra çağrılır. Taslak hazırlama isteği yayınlama
443
+ izni değildir. Yayından kaldırma da canlı akışı durdurur; önceden onay al.
444
+
445
+ #### 5. İleri özellikleri yalnız ihtiyaç varsa ekle
446
+
447
+ - Public/embed: allowed origins tam origin biçimindedir (`https://ornek.com`, trailing
448
+ slash yok). Kullanıcı domaini ve public erişimi açıkça istemeden ekleme/açma.
449
+ - Başlık şablonu: kaydetmeden önce `POST /api/sub-forms/:id/title-preview` ile canlı
450
+ gönderim işlemcisine eşdeğer önizleme al.
451
+ - Korelasyon: önce `GET/POST /api/sub-forms/correlation-groups`; bir alt form yalnız
452
+ tek gruba üye olabilir. Etkin ayarda `groupId`, en az bir `matchField` ve kimlik rolü
453
+ (`customer_id|phone|email`) gerekir. `POST /api/sub-forms/:id/correlation/preview`
454
+ geçmiş gönderiler üstünde dry-run yapar; sonucu görmeden korelasyonu etkinleştirme.
455
+ - Webhook dış sisteme yan etki üretir. Hedef URL/olay kullanıcıya ait karardır; secret
456
+ isteme veya sohbette gösterme. Oluşturma, test ve secret rotasyonu öncesi onay al.
457
+
458
+ Hata kurtarma: `VALIDATION_FAILED` gelirse aynı ucu yeniden `describe_endpoint` ile
459
+ oku, yalnız hatalı alanı düzeltip bir kez dene. `403` için başka binding/modül yoluyla
460
+ yetki aşmaya çalışma. `409` formKey çakışmasında kullanıcı adını değiştirme; aynı
461
+ anlamı koruyan yeni bir key öner. Silme uçları soft-delete olsa bile güvenilir bir
462
+ restore ucu garanti edilmez; kaldırma öncesi kullanıcı onayı al.
463
+
234
464
  ### Rapor al / dışa aktar (report) — `systa_api_call` (no named tool)
235
465
 
236
466
  - No dedicated tool — drive via `systa_api_call`. Önce önizle (`POST /reports/preview`, read-only), sonra dışa aktar (`POST /reports/export`, `format='xlsx'` unless "csv"). `reportType` is the required pivot (request/sla/user/organization/project/lifecycle/subform) — infer from intent. Resolve columns via `GET /reports/columns` if the user only named a type. "bu ay/geçen ay" → dynamic date filter. (For ad-hoc/free queries prefer the `querybuilder_*` tools.)
@@ -238,6 +468,11 @@ Each maps a P0 user verb to a tool/endpoint, with the opinionated defaults from
238
468
  ### Sürüm oluştur (release) — `create_release_list` → `POST /api/releases/lists`
239
469
 
240
470
  - `releaseType='standard'`, `requireAllStakeholderApprovals=false`, `companyId` from scope. Add items with `add_release_item`. Deployment workflow steps (`close-servers` → `start-db-migration` → `start-server-update` → `open-servers` → `complete`) are live ops — **never auto-advance blindly**; show which step you're on and confirm. Release notes: generate → review → publish (never auto-publish).
471
+ - **Bir listenin kimliği = `id` + CalVer sürümü (`versionLabel`, örn. `2026.09.1`) — TARİH DEĞİL.** Aynı şirket **aynı gün İKİ liste** yayınlayabilir, bu yüzden "23 Eylül'deki liste" tek bir listeyi göstermez: tarihle liste arama/eşleştirme güvenilir değildir ve yanlış listeye yazma riski taşır.
472
+ - `create_release_list` yanıtı `versionYear` / `versionMonth` / `versionSeq` / `versionFinalized` alanlarının yanında hazır `versionLabel` döner. **Kullanıcıya listeyi `versionLabel` ile adlandır, kendi tarafında `id`'yi sakla.** `versionFinalized=false` ise numara HENÜZ GEÇİCİDİR (liste onaylanırken değişebilir) — kesinmiş gibi sunma.
473
+ - Sonraki tüm çağrılar listeyi **sayısal `id` ile** hedefler: `add_release_item(listId)`, `get_release_workflow`, `advance_release_workflow(listId)`, `decide_release_stage(itemId)`. Hiçbirinde tarih liste seçmez. `add_release_item`'daki `plannedProdDate` de liste SEÇMEZ; yalnızca **talebin** `plannedProdDate` özel alanını ezer (boşsa listenin deployment tarihinden türetilir).
474
+ - Talep listeye **ID ile** bağlanır (`target_release_list_id`), tarihle değil. Kullanıcı "şu tarihteki sürüme ekle" derse önce o tarihteki listeleri getir, birden fazlaysa `versionLabel` ile **hangisi olduğunu sor** — tahmin etme.
475
+ - Sürüm webhook'larının gövdesi (`release.list.published`, `release.workflow.failed`, `release.item.added` …) `deploymentDate` yanında **her zaman bir `versionLabel` anahtarı** taşır; sürüm çözülemediyse `null` gelir. Dış sisteme sürümden bahsederken bu alanı kullan, `null` ise tarihe düş.
241
476
 
242
477
  ### GitHub: bağla / issue aktar / push (kişisel entegrasyon) — `github.self`
243
478
 
@@ -313,7 +548,7 @@ Gerçek AI kullanımında tespit edilen sürtünme noktaları; her biri canlı d
313
548
  ### Talep açma — `description` = ilk yorum (commentNo 1)
314
549
 
315
550
  - Verdiğin `description` talebin **ilk yorumu** olarak materyalize edilir.
316
- - **Tablo/zengin içerik** için: `systa_api_call POST /requests` ile `descriptionJson` alanına TipTap doc (tablo dahil) gönder — bu da comment #1 olur. **Ayrıca ayrı yorum EKLEME** (çift yorum olur). İçeriği yalnız ayrı yorumda istiyorsan `description` boş bırak.
551
+ - **Tablo/zengin içerik** için: `systa_api_call POST /requests` ile `descriptionJson` alanına TipTap doc (tablo dahil) gönder — bu da comment #1 olur. Düğüm/tablo sözleşmesi ve `#125` / `##1809` referansları yorumla **aynıdır** → [Yorum ekle](#yorum-ekle-comment--add_request_comment--post-apirequestsrequestnumbercomments). **Ayrıca ayrı yorum EKLEME** (çift yorum olur). İçeriği yalnız ayrı yorumda istiyorsan `description` boş bırak.
317
552
 
318
553
  ### Kanban
319
554
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "systa-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "MCP stdio server for SysTa (Talep Takip Sistemi) — lets AI agents (Claude, Codex) use the SysTa REST API via a scoped API key. Zero npm dependencies (native Node).",
5
5
  "type": "commonjs",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -77,6 +77,51 @@ const INSTRUCTIONS = [
77
77
  ' binary tasiyamaz; diger her endpoint icin `systa_api_call` (method+path) kullan — path ve',
78
78
  ' govdeyi describe_endpoint ciktisindan kur (path icin /api on-eki opsiyonel, otomatik normalize edilir).',
79
79
  '',
80
+ 'ALT FORM (sub-form) HAZIRLAMA: Ana talep formu `form`, ek/mini form ise `sub-forms`',
81
+ 'moduludur; `/forms/:id` alt-form detayi DEGILDIR. Once `list_capabilities({module:"sub-forms"})`,',
82
+ 'sonra POST `/sub-forms` + PUT `/sub-forms/:id/save` semalarini `describe_endpoint` ile oku.',
83
+ 'Taslagi POST `/sub-forms` ile isPublic=false olustur; section/field/rule degisikliklerini',
84
+ 'tempId + sectionTempId kullanarak TEK transaction olan PUT `/sub-forms/:id/save` ile kaydet.',
85
+ 'Binding yalniz `embed` veya `request_attach` olur. parentFormId server-authoritative oldugu',
86
+ 'icin gonderme. GET `/sub-forms/:id/preview` render edilmis HTML degil, form/section/field',
87
+ 'onizleme payloadidir. Query/rule/title/correlation preview uclari kalici config yazmaz.',
88
+ 'Kullaniciya taslak ozeti gostermeden ve acik onay almadan publish, public/origin, webhook,',
89
+ 'periodic veya korelasyon ozelligini etkinlestirme; silme/yayindan kaldirma icin de onay al.',
90
+ '',
91
+ 'YORUM YAZMA (comment) — ONEMLI, ZENGIN METIN SOZLESMESI:',
92
+ ' Yorum govdesi DUZ METIN DEGIL, TipTap JSON dokumanidir: {type:"doc", content:[...]}.',
93
+ ' `add_request_comment` duz metni tek paragrafa sarar — TABLO, BASLIK, LISTE, KOD BLOGU',
94
+ ' gibi zengin icerik icin ayni araca `commentTextJson` ile TAM dokumani ver (govde',
95
+ ' verilirse `text` yok sayilir). Ek dosya icin `fileIds`; dosyalar hem yorumda hem',
96
+ ' Dosyalar sekmesinde gorunur.',
97
+ '',
98
+ ' RENDER EDILEN DUGUMLER (otorite: frontend jsonToHtml; listede olmayan dugum SESSIZCE',
99
+ ' yalniz icerigi ile basilir): doc, paragraph, heading{level:1..3}, text, hardBreak,',
100
+ ' bulletList, orderedList, listItem, blockquote, codeBlock{language}, table, tableRow,',
101
+ ' tableHeader, tableCell, mention{id,label}. METIN ISARETLERI (marks): bold, italic,',
102
+ ' underline, strike, code, link{href}, textStyle{color,fontSize,fontFamily}, highlight{color}.',
103
+ '',
104
+ ' TABLO YAPISI (ic ice sira KATIDIR): table > tableRow > (tableHeader | tableCell) >',
105
+ ' paragraph > text. Hucreye dogrudan `text` KOYMA — paragraph katmani atlanirsa hucre',
106
+ ' bos gorunur. Ornek:',
107
+ ' {"type":"table","content":[',
108
+ ' {"type":"tableRow","content":[',
109
+ ' {"type":"tableHeader","content":[{"type":"paragraph","content":[{"type":"text","text":"Bulgu"}]}]},',
110
+ ' {"type":"tableHeader","content":[{"type":"paragraph","content":[{"type":"text","text":"Kart"}]}]}]},',
111
+ ' {"type":"tableRow","content":[',
112
+ ' {"type":"tableCell","content":[{"type":"paragraph","content":[{"type":"text","text":"Filo uyarisi cikmiyor"}]}]},',
113
+ ' {"type":"tableCell","content":[{"type":"paragraph","content":[{"type":"text","text":"##1809"}]}]}]}]}',
114
+ '',
115
+ ' TALEP / GOREV KARTI REFERANSI — hicbir ozel isaret GEREKMEZ, DUZ METIN yeter:',
116
+ ' `#125` -> TALEP referansi (tiklayinca talep onizlemesi acilir)',
117
+ ' `##1809` -> GOREV KARTI referansi (tiklayinca kartin onizlemesi acilir)',
118
+ ' Sayi TALEP icin talep numarasi, KART icin kartin GLOBAL id`sidir (panodaki `#3` gibi',
119
+ ' talep-ici sira numarasi DEGIL; kart id`sini get_request_board / cards listesinden',
120
+ ' `card.id` olarak al). Yorum govdesinde gecen her `#<sayi>` / `##<sayi>` otomatik',
121
+ ' tiklanabilir rozete cevrilir — link mark`i, href veya oznitelik EKLEME.',
122
+ ' UYARI: ayni sayi hem talep hem kart olabilir; oneki karistirmak kullaniciyi YANLIS',
123
+ ' kayda goturur. Kart kastediyorsan CIFT diyez sart.',
124
+ '',
80
125
  'RAPOR / SAYIM / DAGILIM / TOPLAMA niyetinde: list_requests`i elle sayfalayip client-side TOPLAMA YAPMA.',
81
126
  ' Sunucu-tarafli rapor araclari VARDIR: querybuilder_ai_generate_query (dogal dil -> sorgu),',
82
127
  ' querybuilder_execute_query (server-side groupBy/sayim/filtre), querybuilder_describe_fields,',
@@ -1470,26 +1515,79 @@ const TOOLS = [
1470
1515
  },
1471
1516
  {
1472
1517
  name: 'add_request_comment',
1473
- description: `Add a PLAIN-TEXT comment to a SysTa request (talebe yorum ekle). Requires the API key to hold BOTH "request.comment.create" and "request.tabs.comments.view" scopes. The text is wrapped into the rich-text (TipTap) format automatically. For RICH content that renders for the end-user — tables, code blocks, @user mentions — or to attach files, do NOT use this tool: call describe_endpoint("/api/requests/:requestNumber/comments","POST") for the TipTap node contract, then systa_api_call POST /requests/{n}/comments with a full commentTextJson document (and fileIds:[...] for attachments; files appear in both the comment and the Files tab).${FROZEN_NOTE}`,
1518
+ description: `Add a comment to a SysTa request (talebe yorum ekle). Requires the API key to hold BOTH "request.comment.create" and "request.tabs.comments.view" scopes.
1519
+
1520
+ The comment body is a TipTap JSON document, NOT plain text. Two ways to call:
1521
+ * text -> a single paragraph; the tool wraps it for you. Use for ordinary comments.
1522
+ * commentTextJson -> a FULL TipTap doc ({type:"doc",content:[...]}). Use for TABLES, headings, lists, code blocks, @mentions. When given, "text" is ignored.
1523
+ Attach files with fileIds:[...] (they appear in the comment AND the Files tab).
1524
+
1525
+ TABLE nesting is strict: table > tableRow > (tableHeader|tableCell) > paragraph > text. Putting "text" directly in a cell renders an EMPTY cell.
1526
+ Renderable nodes: doc, paragraph, heading{level:1..3}, text, hardBreak, bulletList, orderedList, listItem, blockquote, codeBlock{language}, table, tableRow, tableHeader, tableCell, mention{id,label}. Marks: bold, italic, underline, strike, code, link{href}, textStyle{color,fontSize,fontFamily}, highlight{color}.
1527
+
1528
+ REFERENCES need NO markup — plain text is enough, anywhere in the body:
1529
+ "#125" -> request reference (click opens a request preview)
1530
+ "##1809" -> task card reference (click opens the card preview)
1531
+ The number is the request number for "#", and the card's GLOBAL id for "##" (NOT the per-request "#3" sequence shown on the board — read card.id from get_request_board). Do NOT add a link mark or href; the renderer turns the token into a chip. Mixing the prefixes sends the user to the WRONG record.${FROZEN_NOTE}`,
1474
1532
  inputSchema: {
1475
1533
  type: 'object',
1476
1534
  additionalProperties: false,
1477
- required: ['requestNumber', 'text'],
1535
+ required: ['requestNumber'],
1478
1536
  properties: {
1479
1537
  requestNumber: { type: 'integer', description: 'Request id/number to comment on' },
1480
- text: { type: 'string', description: 'Comment text (plain)' },
1538
+ text: {
1539
+ type: 'string',
1540
+ description:
1541
+ 'Ordinary comment text -> wrapped into a single paragraph. References work as plain text here: "#125" (request), "##1809" (task card). Ignored when commentTextJson is given.',
1542
+ },
1543
+ commentTextJson: {
1544
+ type: 'object',
1545
+ description:
1546
+ 'Full TipTap document for RICH content ({type:"doc",content:[...]}) — tables, headings, lists, code blocks, mentions. Table nesting: table > tableRow > (tableHeader|tableCell) > paragraph > text.',
1547
+ },
1548
+ fileIds: {
1549
+ type: 'array',
1550
+ items: { type: 'integer' },
1551
+ description:
1552
+ 'Ids of already-uploaded files to attach (upload_file_to first). They appear in the comment and in the Files tab.',
1553
+ },
1481
1554
  },
1482
1555
  },
1483
1556
  handler: (args) => {
1484
- const content = String(args.text || '');
1485
- const doc = content
1486
- ? {
1487
- type: 'doc',
1488
- content: [{ type: 'paragraph', content: [{ type: 'text', text: content }] }],
1489
- }
1490
- : { type: 'doc', content: [{ type: 'paragraph' }] };
1557
+ const body = {};
1558
+
1559
+ if (args.commentTextJson) {
1560
+ // Zengin govde verildiginde `text` YOK SAYILIR: ikisini birlestirmek
1561
+ // sessizce iki farkli icerik uretirdi. Sekil dogrulamasi burada yapilir
1562
+ // ki arka uctan donen genel 400 yerine ne bekledigimiz soylensin.
1563
+ const doc = args.commentTextJson;
1564
+ if (doc.type !== 'doc' || !Array.isArray(doc.content)) {
1565
+ throw new Error(
1566
+ 'commentTextJson must be a TipTap document: {"type":"doc","content":[...]}',
1567
+ );
1568
+ }
1569
+ body.commentTextJson = doc;
1570
+ } else {
1571
+ const content = String(args.text || '');
1572
+ body.commentTextJson = content
1573
+ ? {
1574
+ type: 'doc',
1575
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: content }] }],
1576
+ }
1577
+ : { type: 'doc', content: [{ type: 'paragraph' }] };
1578
+ }
1579
+
1580
+ if (Array.isArray(args.fileIds) && args.fileIds.length > 0) {
1581
+ body.fileIds = args.fileIds;
1582
+ }
1583
+
1584
+ // En az biri zorunlu (arka uc de dayatir); bos cagriyi burada yakala.
1585
+ if (!args.commentTextJson && !args.text && !body.fileIds) {
1586
+ throw new Error('Provide at least one of: text, commentTextJson, fileIds');
1587
+ }
1588
+
1491
1589
  return systaFetch('POST', `/requests/${encodeURIComponent(args.requestNumber)}/comments`, {
1492
- body: { commentTextJson: doc },
1590
+ body,
1493
1591
  });
1494
1592
  },
1495
1593
  },
@@ -1513,7 +1611,7 @@ const TOOLS = [
1513
1611
  },
1514
1612
  {
1515
1613
  name: 'change_request_status',
1516
- description: `Move a SysTa request to a target status (durum degistir / tamamla / kapat / yeniden ac), optionally with a closing comment. BEFORE calling, use list_allowed_transitions(requestNumber) to see valid target statuses -- transition rules restrict which statuses are reachable from the current one. Give EITHER statusId OR statusKey (statusKey resolves via GET /statuses; a natural-language user phrase like "test asamasi" maps to statusKey~test). "tamamla/kapat" => a final status (isFinal=true); "yeniden ac" => a non-final one. A given comment is posted right after the transition (plain text, auto-wrapped to TipTap). A status change may trigger a workflow gate/approval -- if the API returns 400 TRANSITION_NOT_ALLOWED read the error.details, pick a valid target and retry; do NOT retry blindly.${FROZEN_NOTE}`,
1614
+ description: `Move a SysTa request to a target status (durum degistir / tamamla / kapat / yeniden ac), optionally with a closing comment. BEFORE calling, use list_allowed_transitions(requestNumber) to see valid target statuses -- transition rules restrict which statuses are reachable from the current one. Give EITHER statusId OR statusKey (statusKey resolves via GET /statuses; a natural-language user phrase like "test asamasi" maps to statusKey~test). "tamamla/kapat" => a final status (isFinal=true); "yeniden ac" => a non-final one. A given comment is posted right after the transition (plain text, auto-wrapped to TipTap; plain "#125" / "##1809" tokens become clickable request / task-card chips). For a RICH closing note (table, list) call add_request_comment with commentTextJson AFTER the transition instead. A status change may trigger a workflow gate/approval -- if the API returns 400 TRANSITION_NOT_ALLOWED read the error.details, pick a valid target and retry; do NOT retry blindly.${FROZEN_NOTE}`,
1517
1615
  inputSchema: {
1518
1616
  type: 'object',
1519
1617
  additionalProperties: false,
@@ -2563,14 +2661,29 @@ const TOOLS = [
2563
2661
  {
2564
2662
  name: 'create_release_list',
2565
2663
  description:
2566
- "Create a release list (surum listesi) for a company on a deployment date. releaseType 'standard' (default) | 'emergency' (+emergencyJustification). companyId must be a resolved numeric id.",
2664
+ 'Create a release list (surum listesi) for a company. A list is identified by its numeric ' +
2665
+ 'id and its CalVer version label (versionLabel, e.g. "2026.09.1") -- NOT by its deployment ' +
2666
+ 'date: the same company can publish TWO lists on the SAME day, so a date does NOT identify ' +
2667
+ 'a list and searching/filtering lists by date is NOT reliable. deploymentDate only says ' +
2668
+ 'WHEN the release ships. The response carries versionYear / versionMonth / versionSeq / ' +
2669
+ 'versionFinalized plus the ready-made versionLabel: quote versionLabel when you name the ' +
2670
+ 'list to the user, and keep the numeric id for every follow-up call (add_release_item, ' +
2671
+ 'get_release_workflow, advance_release_workflow, decide_release_stage). ' +
2672
+ 'versionFinalized=false means the sequence number is still PROVISIONAL and may change when ' +
2673
+ "the list is confirmed -- say so instead of promising a final number. releaseType 'standard' " +
2674
+ "(default) | 'emergency' (+emergencyJustification). companyId must be a resolved numeric id.",
2567
2675
  inputSchema: {
2568
2676
  type: 'object',
2569
2677
  additionalProperties: false,
2570
2678
  required: ['companyId', 'deploymentDate'],
2571
2679
  properties: {
2572
2680
  companyId: { type: 'integer' },
2573
- deploymentDate: { type: 'string', description: 'ISO date/datetime' },
2681
+ deploymentDate: {
2682
+ type: 'string',
2683
+ description:
2684
+ 'ISO date/datetime -- WHEN the release ships. Not an identifier: several lists may ' +
2685
+ 'share one deployment date.',
2686
+ },
2574
2687
  plannedStartTime: { type: 'string', description: 'HH:mm' },
2575
2688
  requireAllStakeholderApprovals: { type: 'boolean' },
2576
2689
  releaseType: { type: 'string', enum: ['standard', 'emergency'] },
@@ -2590,7 +2703,13 @@ const TOOLS = [
2590
2703
  {
2591
2704
  name: 'add_release_item',
2592
2705
  description:
2593
- "Add a request to a release list as a release item (POST /releases/items). Pass 'items' (array) for a batch, or a single listId+requestId. Resolve developer/analyst/stakeholder ids by name.",
2706
+ 'Add a request to a release list as a release item (POST /releases/items). The target list ' +
2707
+ 'is chosen ONLY by its numeric listId -- never by a date: two lists can share the same ' +
2708
+ 'deployment date, so no date parameter here selects, matches or moves a list. Take the id ' +
2709
+ 'from create_release_list or from the release list endpoints, and name the list to the user ' +
2710
+ 'by its CalVer versionLabel (e.g. "2026.09.1"). ' +
2711
+ "Pass 'items' (array) for a batch, or a single listId+requestId. Resolve " +
2712
+ 'developer/analyst/stakeholder ids by name.',
2594
2713
  inputSchema: {
2595
2714
  type: 'object',
2596
2715
  additionalProperties: false,
@@ -2598,7 +2717,14 @@ const TOOLS = [
2598
2717
  properties: {
2599
2718
  listId: { type: 'integer' },
2600
2719
  requestId: { type: 'integer' },
2601
- plannedProdDate: { type: 'string', description: 'ISO date' },
2720
+ plannedProdDate: {
2721
+ type: 'string',
2722
+ description:
2723
+ 'ISO date. Does NOT select, match or move a list -- the list is listId ONLY. It just ' +
2724
+ "overrides the REQUEST's plannedProdDate custom field, which otherwise defaults to " +
2725
+ "the linked list's deployment date. Omit it unless the request must carry a date " +
2726
+ 'different from its list.',
2727
+ },
2602
2728
  developerIds: { type: 'array', items: { type: 'integer' } },
2603
2729
  analystIds: { type: 'array', items: { type: 'integer' } },
2604
2730
  stakeholderIds: { type: 'array', items: { type: 'integer' } },
@@ -4332,6 +4458,7 @@ if (require.main === module) {
4332
4458
  }
4333
4459
 
4334
4460
  module.exports = {
4461
+ INSTRUCTIONS,
4335
4462
  TOOLS,
4336
4463
  TOOL_MAP,
4337
4464
  TOOL_REQUIRED_PERMISSION,