systa-mcp 1.0.7 → 1.0.8

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 +6 -6
  2. package/SKILL.md +18 -18
  3. package/package.json +1 -1
  4. package/server.js +20 -5
package/README.md CHANGED
@@ -74,12 +74,12 @@ npx -y systa-mcp --install-skill # -> ~/.claude/skills/systa/SKILL.md
74
74
 
75
75
  ### Çevre değişkenleri
76
76
 
77
- | Değişken | Örnek | Açıklama |
78
- | ------------------------ | ------------------------------- | --------------------------------------------------------------------------------------------------- |
79
- | `SYSTA_API_BASE_URL` | `https://systa.example.com/api` | API kök adresi (`/api` dahil), nötr host |
80
- | `SYSTA_API_KEY` | `sk_live_<vendor>_...` | Profilden üretilen anahtar (bir kez gösterilir) |
81
- | `SYSTA_VENDOR_CODE` | `vizyoneks` | OPSIYONEL — verilirse anahtarın vendor'ıyla aynı olmalı; boş bırakılırsa backend anahtardan türetir |
82
- | `SYSTA_MCP_STRICT_TOOLS` | `1` | OPSIYONEL — kapsam çözülemezse tüm aksiyon araçlarını gizle (fail-closed) |
77
+ | Değişken | Örnek | Açıklama |
78
+ | ------------------------ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
79
+ | `SYSTA_API_BASE_URL` | `https://systa.example.com/api` | API kök adresi (`/api` dahil), nötr host |
80
+ | `SYSTA_API_KEY` | `sk_live_<vendor>_...` | Profilden üretilen anahtar (bir kez gösterilir) |
81
+ | `SYSTA_VENDOR_CODE` | `vizyoneks` | OPSIYONEL — verilirse anahtarın vendor'ıyla aynı olmalı; boş bırakılırsa backend anahtardan türetir |
82
+ | `SYSTA_MCP_STRICT_TOOLS` | `1` | OPSIYONEL — kapsam çözülemezse tüm aksiyon araçlarını gizle. Değer TAM olarak `1` olmalı; aksi halde fail-open (kapsam çözülemese de araçlar gösterilir) |
83
83
 
84
84
  ### Yerel geliştirme / yerel klona karşı
85
85
 
package/SKILL.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- version: 0.1.0
2
+ version: 1.0.8
3
3
  name: systa
4
4
  description: |
5
5
  Drive SysTa (Talep Takip Sistemi / SysTa) — a
@@ -97,7 +97,7 @@ veya `describe_endpoint` ile gövdeyi netleştir.
97
97
  | Doğal dil (alias) | Modül | Birincil giriş |
98
98
  | --------------------------------------------------------- | --------------- | ----------------------------------------------- |
99
99
  | talep, talepler, is, destek talebi | request | list_requests / create_request |
100
- | gorev, task, kart, card | kanban | create_kanban_card / move_kanban_card |
100
+ | gorev, task, kart, card | kanban | manage_request_card / move_request_card |
101
101
  | pano, board, kanban / kolon, column, asama, liste | kanban | get_request_board |
102
102
  | efor, durum, oncelik, atanan, planla, denetim, gecmis | request | get_request / list_requests |
103
103
  | proje, klonla | project | list_projects / create_project |
@@ -105,13 +105,13 @@ veya `describe_endpoint` ile gövdeyi netleştir.
105
105
  | onay, onayla, reddet | approvals | list_pending_approvals / respond_to_approval |
106
106
  | sla, sure taahhudu | sla | (sla endpoints) |
107
107
  | release, surum, go/no-go, deployment | release | create_release_list / add_release_item |
108
- | musaitlik, izin (leave), vekil | user/automation | create_leave_request |
109
- | vendor, sirket, musteri | company | list_companies |
108
+ | musaitlik, izin (leave), vekil | user/automation | systa_api_call /user-availability, /user-leaves |
109
+ | vendor, sirket, musteri | company | systa_api_call GET /companies |
110
110
  | organizasyon birimi, departman, bolum, birim | department | GET /api/departments |
111
111
  | takim, ekip, team | team | GET /api/teams |
112
112
  | yetki paketi, izin (permission) | security | (bundle/permission endpoints) |
113
- | alt form, sub-form | forms | get_form_schema |
114
- | rapor, raporlama, disa aktar, export, excel, csv | report | preview_report / export_report |
113
+ | alt form, sub-form | forms | systa_api_call GET /forms/:id |
114
+ | rapor, raporlama, disa aktar, export, excel, csv | report | systa_api_call /reports/preview,/reports/export |
115
115
  | ozel sorgu, sorgu, query builder, ad-hoc, veri cek | query-builder | querybuilder_describe_fields / \_execute_query |
116
116
  | hesap tablosu, gridpad, tablo, excel, spreadsheet, hucre | spreadsheet | GET /api/spreadsheets |
117
117
  | dosya, ek, attachment, yukle, upload, indir, download | file | upload_file_to / download_request_file |
@@ -125,7 +125,7 @@ veya `describe_endpoint` ile gövdeyi netleştir.
125
125
  | github, repo, pull request, commit, entegrasyon | github | GET /api/github/connection |
126
126
  | gosterge paneli, dashboard, widget, ozet, grafik, panel | dashboard | GET /api/dashboard/init |
127
127
 
128
- > Bu tablo katalogdaki ~44 modülden kullanıcı-yüzeyli olanları kapsar. Listede olmayan
128
+ > Bu tablo, katalogdaki ~25 modülün kullanıcı-yüzeyli olanlarından bir alt küme kapsar. Listede olmayan
129
129
  > bir kelime için `list_capabilities` çağır — anahtarının erişebildiği güncel modül/alias
130
130
  > kümesini döndürür (kapsam-filtreli; yetkisiz modül görünmez).
131
131
 
@@ -135,7 +135,7 @@ veya `describe_endpoint` ile gövdeyi netleştir.
135
135
  2. **No jargon.** Don't narrate "calling describe_endpoint", "POST /api/requests", "resolving statusId". Speak in the product's user verbs.
136
136
  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.
137
137
  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.
138
- 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 (`get_form_schema`) so required fields are known.
138
+ 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.
139
139
  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.
140
140
  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.
141
141
 
@@ -185,13 +185,13 @@ Each maps a P0 user verb to a tool/endpoint, with the opinionated defaults from
185
185
 
186
186
  - List: `GET /api/projects` (default `page=1, limit=20`, recent first). "durum/sağlık" → combine `health-score` + `risk-score` + `sla-status` into one summary.
187
187
 
188
- ### Kanban kart ekle (add a task card) — `create_kanban_card` → `POST /api/task-boards/:boardId/cards`
188
+ ### Kanban kart ekle (add a task card) — `manage_request_card` (create)
189
189
 
190
- - `boardId` is always tied to a `requestId` — first resolve the board via the request's kanban hierarchy (`GET /api/requests/:requestId/kanban/hierarchy`). Defaults: first/entry column if `columnId` omitted, `priority='normal'`, unassigned unless "bana ata", `cardTitle` min 3 chars (ask to clarify if shorter).
190
+ - `boardId` is always tied to a `requestId` — first resolve the board (`get_request_board`, or `GET /api/requests/:requestId/kanban/hierarchy`). Defaults: first/entry column if `columnId` omitted, `priority='normal'`, unassigned unless "bana ata", `cardTitle` min 3 chars (ask to clarify if shorter). `manage_request_card` handles create / update / complete (the action is in the body) — call `describe_endpoint` if unsure of the fields.
191
191
 
192
- ### Kartı taşı / tamamla (move / complete card) — `move_kanban_card` / `complete_kanban_card`
192
+ ### Kartı taşı / tamamla (move / complete card) — `move_request_card` / `manage_request_card`
193
193
 
194
- - Move: `PUT /api/task-boards/:boardId/cards/:cardId/move`; resolve the target column name → `columnId` from board detail; append to end if no position given. Complete: `PUT .../complete` — server sets `isCompleted=true`, 100%.
194
+ - Move: `move_request_card` resolve the target column name → `columnId` from the board detail; append to end if no position given. Complete: `manage_request_card` with the complete action — server sets `isCompleted=true`, 100%.
195
195
 
196
196
  ### Onaya sun / onayla / bekleyenler (approvals)
197
197
 
@@ -199,13 +199,13 @@ Each maps a P0 user verb to a tool/endpoint, with the opinionated defaults from
199
199
  - Respond: `respond_to_approval` → `POST /api/approvals/responses/:id/respond`; "onayla"→`approved`, "reddet"→`rejected` (rette kısa sebep iste, zorunlu değil).
200
200
  - Pending: `list_pending_approvals` → `GET /api/approvals/requests-awaiting` (mevcut kullanıcının onayını bekleyenler).
201
201
 
202
- ### İzin talebi (leave) — `create_leave_request` `POST /api/user-leaves`
202
+ ### İzin talebi (leave) — `systa_api_call` `POST /user-leaves` (no named tool)
203
203
 
204
- - `userId`=self (sormadan), `leaveType='annual'`, `isHalfDay=false`, dates normalized to `YYYY-MM-DD` ("yarın/cuma" → tarih), `returnToWorkDate` = next working day after end. "izinlerim" → `list_leaves` with `userId=self`.
204
+ - 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.
205
205
 
206
- ### Rapor al / dışa aktar (report) — `preview_report` / `export_report`
206
+ ### Rapor al / dışa aktar (report) — `systa_api_call` (no named tool)
207
207
 
208
- - Önce önizle (`POST /api/reports/preview`, read-only), sonra dışa aktar (`POST /api/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 /api/reports/columns` if the user only named a type. "bu ay/geçen ay" → dynamic date filter.
208
+ - 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.)
209
209
 
210
210
  ### Sürüm oluştur (release) — `create_release_list` → `POST /api/releases/lists`
211
211
 
@@ -221,8 +221,8 @@ SysTa returns structured errors. Recover deterministically instead of giving up.
221
221
  - If it's genuinely user-owned (a `title`, a date, an approver name, a secret), **ask the user for exactly that field** — quote the human-friendly meaning, not the raw key.
222
222
  - Retry once after a fix. If it fails again with the same field, surface it to the user rather than looping.
223
223
  2. **`UNEXPECTED_FILE_FIELD` / `UNKNOWN_PARAM`** — you sent a flag/field the endpoint doesn't accept. Re-check with `describe_endpoint` and drop it. (Web/UI-only fields aren't valid on the API surface.)
224
- 3. **`404` on a `:requestNumber` / `:id`** — the handle didn't resolve. Run the matching list/search (`list_requests`, `list_projects`, `list_users?search=`, `search_tags`) to map name/number → id, then retry. Never hard-code an id.
225
- 4. **ID resolution chain (general).** Most mutations need an id the user gave you as a name: company → `list_companies`, team → `list_teams` (name→`teamCode`), user → `list_users?search=` (name→`userId`, "ben/bana"→self), status → `GET /api/statuses` (name→`statusId`), tag → `search_tags`, board → request's kanban hierarchy (requestId→`boardId`→`columnId`). Resolve first, act second.
224
+ 3. **`404` on a `:requestNumber` / `:id`** — the handle didn't resolve. Run the matching list/search (`list_requests`, `list_projects`, or `systa_api_call` `GET /users?search=`, `GET /tags/search`) to map name/number → id, then retry. Never hard-code an id.
225
+ 4. **ID resolution chain (general).** Most mutations need an id the user gave you as a name. There are NO dedicated lookup tools — use `systa_api_call`: company → `GET /companies`, team → `GET /teams` (name→`teamCode`), user → `GET /users?search=` (name→`userId`, "ben/bana"→self via who-am-i), status → `GET /statuses` (name→`statusId`), tag → `GET /tags/search`, board → request's kanban hierarchy (`get_request_board`; requestId→`boardId`→`columnId`). Resolve first, act second.
226
226
  5. **`401/403`** — expired or out-of-scope key. Don't retry blindly; tell the user the auth/permission problem (in their language) and stop. For `403 permission`, name the capability the key lacks; don't attempt a privilege workaround.
227
227
  6. **`409 conflict`** (optimistic concurrency on cards/edits) — re-read the entity to get the latest `updatedAt`, then retry the mutation once with the fresh value; warn the user only if it conflicts again.
228
228
  7. **Dual-path "list vs create" endpoints** (e.g. `POST /api/sla/definitions`, `POST /api/sla/standby-configs`) — an empty/filter-only body lists; a full body creates. Choose the body shape that matches the intent; don't accidentally create when the user asked to list.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "systa-mcp",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
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
@@ -29,7 +29,9 @@ const path = require('node:path');
29
29
 
30
30
  const PROTOCOL_VERSION = '2024-11-05';
31
31
  const SERVER_NAME = 'systa-mcp';
32
- const SERVER_VERSION = '1.0.3';
32
+ // Tek kaynak: surum package.json'dan okunur (drift onleme — server.js/SKILL.md/package.json
33
+ // ayni surumu gosterir). npm yayinlanan pakete package.json'i daima dahil eder.
34
+ const SERVER_VERSION = require('./package.json').version;
33
35
 
34
36
  // Server-seviye rehber — her oturumda AI ajanin context'ine girer (kisa tutulur).
35
37
  // Ajana SysTa'yi nasil kesfedip kullanacagini ogretir; detay lazy araclardadir.
@@ -528,11 +530,11 @@ const TOOLS = [
528
530
  {
529
531
  name: 'create_request',
530
532
  description:
531
- 'Create a SysTa request (talep). Requires the "request.create" scope. Returns the created request under data.request. PROACTIVE (recommended): call prepare_create_request(companyId) FIRST -- it returns the active form\'s required fields AND valid ids (statuses/categories/organizations) so you fill them up front and avoid the 400 roundtrip. The required set is DYNAMIC: title/companyId/statusId are the baseline (+assignedTo for assignment), but this vendor\'s ACTIVE REQUEST FORM may require more system fields (e.g. categoryId, organizationId). If you skip prepare_, on a missing field the API returns 400 VALIDATION_FAILED naming it via details[].fieldKey -- add it and retry. For fields beyond this tool, use systa_api_call POST /requests. ONEMLI (talep 166 #60): verdigin `description` talebin ILK YORUMU (commentNo 1) olarak materyalize edilir; ZENGIN icerik istiyorsan (tablo/kod blogu/bicimli metin) `description` yerine systa_api_call POST /requests ile `descriptionJson` alanina TipTap doc gonder (tablo dahil) -- bu da comment #1 olur. Yani tabloyu talep ACARKEN descriptionJson icinde ver; AYRICA ayri bir yorum EKLEME (yoksa cift yorum olusur). Icerigi yalnizca ayri yorumda istiyorsan description bos birak.',
533
+ 'Create a SysTa request (talep). Requires the "request.create" scope. Returns the created request under data.request. PROACTIVE (recommended): call prepare_create_request(companyId) FIRST -- it returns the active form\'s required fields AND valid ids (statuses/categories/organizations) so you fill them up front and avoid the 400 roundtrip. The required set is DYNAMIC: title/companyId/statusId are the baseline (+ at least one assignment: assignedTo OR assignedTeams), but this vendor\'s ACTIVE REQUEST FORM may require more system fields (e.g. categoryId, organizationId). If you skip prepare_, on a missing field the API returns 400 VALIDATION_FAILED naming it via details[].fieldKey -- add it and retry. For fields beyond this tool, use systa_api_call POST /requests. ONEMLI (talep 166 #60): verdigin `description` talebin ILK YORUMU (commentNo 1) olarak materyalize edilir; ZENGIN icerik istiyorsan (tablo/kod blogu/bicimli metin) `description` yerine systa_api_call POST /requests ile `descriptionJson` alanina TipTap doc gonder (tablo dahil) -- bu da comment #1 olur. Yani tabloyu talep ACARKEN descriptionJson icinde ver; AYRICA ayri bir yorum EKLEME (yoksa cift yorum olusur). Icerigi yalnizca ayri yorumda istiyorsan description bos birak.',
532
534
  inputSchema: {
533
535
  type: 'object',
534
536
  additionalProperties: false,
535
- required: ['title', 'companyId', 'statusId', 'assignedTo'],
537
+ required: ['title', 'companyId', 'statusId'],
536
538
  properties: {
537
539
  title: { type: 'string', description: 'Request title' },
538
540
  description: { type: 'string', description: 'Request description (plain text)' },
@@ -540,7 +542,18 @@ const TOOLS = [
540
542
  statusId: { type: 'integer', description: 'Initial status id' },
541
543
  assignedTo: {
542
544
  type: 'integer',
543
- description: 'Assignee user id (an assignment is required by SysTa)',
545
+ description:
546
+ 'Assignee user id. SysTa requires at least ONE assignment: provide assignedTo (a user) OR assignedTeams (teams).',
547
+ },
548
+ assignedTeams: {
549
+ type: 'array',
550
+ items: { type: 'integer' },
551
+ description:
552
+ 'Assignee team ids (alternative/addition to assignedTo; at least one of assignedTo/assignedTeams is required).',
553
+ },
554
+ channel: {
555
+ type: 'string',
556
+ description: "Source channel; defaults to 'API' for AI/MCP-created requests.",
544
557
  },
545
558
  categoryId: {
546
559
  type: 'integer',
@@ -560,7 +573,9 @@ const TOOLS = [
560
573
  description: args.description ?? '',
561
574
  companyId: args.companyId,
562
575
  statusId: args.statusId,
563
- assignedTo: args.assignedTo,
576
+ channel: args.channel ?? 'API',
577
+ ...(args.assignedTo != null ? { assignedTo: args.assignedTo } : {}),
578
+ ...(args.assignedTeams != null ? { assignedTeams: args.assignedTeams } : {}),
564
579
  ...(args.categoryId != null ? { categoryId: args.categoryId } : {}),
565
580
  ...(args.organizationId != null ? { organizationId: args.organizationId } : {}),
566
581
  },