@mcp-abap-adt/calm-server 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +48 -25
  3. package/dist/bin/stdio.js +9 -6
  4. package/dist/bin/stdio.js.map +1 -1
  5. package/dist/server/config.d.ts.map +1 -1
  6. package/dist/server/config.js +7 -1
  7. package/dist/server/config.js.map +1 -1
  8. package/dist/server/runStdio.d.ts +2 -4
  9. package/dist/server/runStdio.d.ts.map +1 -1
  10. package/dist/server/runStdio.js +15 -5
  11. package/dist/server/runStdio.js.map +1 -1
  12. package/dist/server/stderrLogger.d.ts +24 -0
  13. package/dist/server/stderrLogger.d.ts.map +1 -0
  14. package/dist/server/stderrLogger.js +44 -0
  15. package/dist/server/stderrLogger.js.map +1 -0
  16. package/dist/tools/documents/createDocument.d.ts +5 -0
  17. package/dist/tools/documents/createDocument.d.ts.map +1 -0
  18. package/dist/tools/documents/createDocument.js +48 -0
  19. package/dist/tools/documents/createDocument.js.map +1 -0
  20. package/dist/tools/documents/deleteDocument.d.ts +10 -0
  21. package/dist/tools/documents/deleteDocument.d.ts.map +1 -0
  22. package/dist/tools/documents/deleteDocument.js +35 -0
  23. package/dist/tools/documents/deleteDocument.js.map +1 -0
  24. package/dist/tools/documents/index.d.ts +10 -0
  25. package/dist/tools/documents/index.d.ts.map +1 -1
  26. package/dist/tools/documents/index.js +21 -1
  27. package/dist/tools/documents/index.js.map +1 -1
  28. package/dist/tools/documents/listDocumentStatuses.d.ts +7 -0
  29. package/dist/tools/documents/listDocumentStatuses.d.ts.map +1 -0
  30. package/dist/tools/documents/listDocumentStatuses.js +23 -0
  31. package/dist/tools/documents/listDocumentStatuses.js.map +1 -0
  32. package/dist/tools/documents/listDocumentTypes.d.ts +7 -0
  33. package/dist/tools/documents/listDocumentTypes.d.ts.map +1 -0
  34. package/dist/tools/documents/listDocumentTypes.js +23 -0
  35. package/dist/tools/documents/listDocumentTypes.js.map +1 -0
  36. package/dist/tools/documents/updateDocument.d.ts +7 -0
  37. package/dist/tools/documents/updateDocument.d.ts.map +1 -0
  38. package/dist/tools/documents/updateDocument.js +43 -0
  39. package/dist/tools/documents/updateDocument.js.map +1 -0
  40. package/dist/tools/features/createExternalReference.d.ts +5 -0
  41. package/dist/tools/features/createExternalReference.d.ts.map +1 -0
  42. package/dist/tools/features/createExternalReference.js +49 -0
  43. package/dist/tools/features/createExternalReference.js.map +1 -0
  44. package/dist/tools/features/deleteExternalReference.d.ts +12 -0
  45. package/dist/tools/features/deleteExternalReference.d.ts.map +1 -0
  46. package/dist/tools/features/deleteExternalReference.js +44 -0
  47. package/dist/tools/features/deleteExternalReference.js.map +1 -0
  48. package/dist/tools/features/index.d.ts +6 -0
  49. package/dist/tools/features/index.d.ts.map +1 -1
  50. package/dist/tools/features/index.js +13 -1
  51. package/dist/tools/features/index.js.map +1 -1
  52. package/dist/tools/features/listExternalReferences.d.ts +11 -0
  53. package/dist/tools/features/listExternalReferences.d.ts.map +1 -0
  54. package/dist/tools/features/listExternalReferences.js +47 -0
  55. package/dist/tools/features/listExternalReferences.js.map +1 -0
  56. package/dist/tools/hierarchy/createHierarchyNode.d.ts +5 -0
  57. package/dist/tools/hierarchy/createHierarchyNode.d.ts.map +1 -0
  58. package/dist/tools/hierarchy/createHierarchyNode.js +46 -0
  59. package/dist/tools/hierarchy/createHierarchyNode.js.map +1 -0
  60. package/dist/tools/hierarchy/deleteHierarchyNode.d.ts +10 -0
  61. package/dist/tools/hierarchy/deleteHierarchyNode.d.ts.map +1 -0
  62. package/dist/tools/hierarchy/deleteHierarchyNode.js +35 -0
  63. package/dist/tools/hierarchy/deleteHierarchyNode.js.map +1 -0
  64. package/dist/tools/hierarchy/index.d.ts +6 -0
  65. package/dist/tools/hierarchy/index.d.ts.map +1 -1
  66. package/dist/tools/hierarchy/index.js +13 -1
  67. package/dist/tools/hierarchy/index.js.map +1 -1
  68. package/dist/tools/hierarchy/listHierarchy.d.ts.map +1 -1
  69. package/dist/tools/hierarchy/listHierarchy.js +4 -2
  70. package/dist/tools/hierarchy/listHierarchy.js.map +1 -1
  71. package/dist/tools/hierarchy/updateHierarchyNode.d.ts +7 -0
  72. package/dist/tools/hierarchy/updateHierarchyNode.d.ts.map +1 -0
  73. package/dist/tools/hierarchy/updateHierarchyNode.js +38 -0
  74. package/dist/tools/hierarchy/updateHierarchyNode.js.map +1 -0
  75. package/dist/tools/logs/index.d.ts +2 -0
  76. package/dist/tools/logs/index.d.ts.map +1 -1
  77. package/dist/tools/logs/index.js +8 -2
  78. package/dist/tools/logs/index.js.map +1 -1
  79. package/dist/tools/logs/postLogs.d.ts +10 -0
  80. package/dist/tools/logs/postLogs.d.ts.map +1 -0
  81. package/dist/tools/logs/postLogs.js +58 -0
  82. package/dist/tools/logs/postLogs.js.map +1 -0
  83. package/dist/tools/projects/createProject.d.ts +5 -0
  84. package/dist/tools/projects/createProject.d.ts.map +1 -0
  85. package/dist/tools/projects/createProject.js +42 -0
  86. package/dist/tools/projects/createProject.js.map +1 -0
  87. package/dist/tools/projects/getProgram.d.ts +7 -0
  88. package/dist/tools/projects/getProgram.d.ts.map +1 -0
  89. package/dist/tools/projects/getProgram.js +32 -0
  90. package/dist/tools/projects/getProgram.js.map +1 -0
  91. package/dist/tools/projects/index.d.ts +10 -0
  92. package/dist/tools/projects/index.d.ts.map +1 -1
  93. package/dist/tools/projects/index.js +21 -1
  94. package/dist/tools/projects/index.js.map +1 -1
  95. package/dist/tools/projects/listPrograms.d.ts +10 -0
  96. package/dist/tools/projects/listPrograms.d.ts.map +1 -0
  97. package/dist/tools/projects/listPrograms.js +36 -0
  98. package/dist/tools/projects/listPrograms.js.map +1 -0
  99. package/dist/tools/projects/listTeamMembers.d.ts +11 -0
  100. package/dist/tools/projects/listTeamMembers.d.ts.map +1 -0
  101. package/dist/tools/projects/listTeamMembers.js +49 -0
  102. package/dist/tools/projects/listTeamMembers.js.map +1 -0
  103. package/dist/tools/projects/listTimeboxes.d.ts +11 -0
  104. package/dist/tools/projects/listTimeboxes.d.ts.map +1 -0
  105. package/dist/tools/projects/listTimeboxes.js +49 -0
  106. package/dist/tools/projects/listTimeboxes.js.map +1 -0
  107. package/dist/tools/tasks/createTask.d.ts +5 -0
  108. package/dist/tools/tasks/createTask.d.ts.map +1 -0
  109. package/dist/tools/tasks/createTask.js +58 -0
  110. package/dist/tools/tasks/createTask.js.map +1 -0
  111. package/dist/tools/tasks/createTaskComment.d.ts +7 -0
  112. package/dist/tools/tasks/createTaskComment.d.ts.map +1 -0
  113. package/dist/tools/tasks/createTaskComment.js +36 -0
  114. package/dist/tools/tasks/createTaskComment.js.map +1 -0
  115. package/dist/tools/tasks/deleteTask.d.ts +10 -0
  116. package/dist/tools/tasks/deleteTask.d.ts.map +1 -0
  117. package/dist/tools/tasks/deleteTask.js +35 -0
  118. package/dist/tools/tasks/deleteTask.js.map +1 -0
  119. package/dist/tools/tasks/index.d.ts +14 -0
  120. package/dist/tools/tasks/index.d.ts.map +1 -1
  121. package/dist/tools/tasks/index.js +29 -1
  122. package/dist/tools/tasks/index.js.map +1 -1
  123. package/dist/tools/tasks/listDeliverables.d.ts +11 -0
  124. package/dist/tools/tasks/listDeliverables.d.ts.map +1 -0
  125. package/dist/tools/tasks/listDeliverables.js +49 -0
  126. package/dist/tools/tasks/listDeliverables.js.map +1 -0
  127. package/dist/tools/tasks/listTaskReferences.d.ts +11 -0
  128. package/dist/tools/tasks/listTaskReferences.d.ts.map +1 -0
  129. package/dist/tools/tasks/listTaskReferences.js +46 -0
  130. package/dist/tools/tasks/listTaskReferences.js.map +1 -0
  131. package/dist/tools/tasks/listWorkstreams.d.ts +11 -0
  132. package/dist/tools/tasks/listWorkstreams.d.ts.map +1 -0
  133. package/dist/tools/tasks/listWorkstreams.js +49 -0
  134. package/dist/tools/tasks/listWorkstreams.js.map +1 -0
  135. package/dist/tools/tasks/updateTask.d.ts +7 -0
  136. package/dist/tools/tasks/updateTask.d.ts.map +1 -0
  137. package/dist/tools/tasks/updateTask.js +44 -0
  138. package/dist/tools/tasks/updateTask.js.map +1 -0
  139. package/dist/tools/testCases/createTestAction.d.ts +5 -0
  140. package/dist/tools/testCases/createTestAction.d.ts.map +1 -0
  141. package/dist/tools/testCases/createTestAction.js +54 -0
  142. package/dist/tools/testCases/createTestAction.js.map +1 -0
  143. package/dist/tools/testCases/createTestActivity.d.ts +5 -0
  144. package/dist/tools/testCases/createTestActivity.d.ts.map +1 -0
  145. package/dist/tools/testCases/createTestActivity.js +46 -0
  146. package/dist/tools/testCases/createTestActivity.js.map +1 -0
  147. package/dist/tools/testCases/createTestCase.d.ts +5 -0
  148. package/dist/tools/testCases/createTestCase.d.ts.map +1 -0
  149. package/dist/tools/testCases/createTestCase.js +42 -0
  150. package/dist/tools/testCases/createTestCase.js.map +1 -0
  151. package/dist/tools/testCases/deleteTestCase.d.ts +10 -0
  152. package/dist/tools/testCases/deleteTestCase.d.ts.map +1 -0
  153. package/dist/tools/testCases/deleteTestCase.js +35 -0
  154. package/dist/tools/testCases/deleteTestCase.js.map +1 -0
  155. package/dist/tools/testCases/index.d.ts +14 -0
  156. package/dist/tools/testCases/index.d.ts.map +1 -1
  157. package/dist/tools/testCases/index.js +29 -1
  158. package/dist/tools/testCases/index.js.map +1 -1
  159. package/dist/tools/testCases/listTestActions.d.ts +11 -0
  160. package/dist/tools/testCases/listTestActions.d.ts.map +1 -0
  161. package/dist/tools/testCases/listTestActions.js +45 -0
  162. package/dist/tools/testCases/listTestActions.js.map +1 -0
  163. package/dist/tools/testCases/listTestActivities.d.ts +11 -0
  164. package/dist/tools/testCases/listTestActivities.d.ts.map +1 -0
  165. package/dist/tools/testCases/listTestActivities.js +45 -0
  166. package/dist/tools/testCases/listTestActivities.js.map +1 -0
  167. package/dist/tools/testCases/listTestCases.d.ts.map +1 -1
  168. package/dist/tools/testCases/listTestCases.js +5 -7
  169. package/dist/tools/testCases/listTestCases.js.map +1 -1
  170. package/dist/tools/testCases/updateTestCase.d.ts +7 -0
  171. package/dist/tools/testCases/updateTestCase.d.ts.map +1 -0
  172. package/dist/tools/testCases/updateTestCase.js +38 -0
  173. package/dist/tools/testCases/updateTestCase.js.map +1 -0
  174. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,108 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 — 2026-05-13
4
+
5
+ Same-day follow-up to 0.2.0: realigns two bonus tools with
6
+ `@mcp-abap-adt/calm-client@0.2.0`, which corrected its
7
+ `listDeliverables` / `listWorkstreams` signatures to require
8
+ `projectId` (see calm-client issue #1 / PR #2).
9
+
10
+ ### Changed (BREAKING for two tool input schemas)
11
+
12
+ - **`calm_tasks_list_deliverables`** — `projectId` is now `required`
13
+ in the input schema. The Tasks service exposes this endpoint with
14
+ `@RequestParam UUID projectId` and rejects calls without it; the
15
+ previous schema treated `projectId` as optional and emitted it as an
16
+ OData `$filter`, which the server ignored (sandbox returned 400,
17
+ live tenant would also 400).
18
+ - **`calm_tasks_list_workstreams`** — same change for symmetry and to
19
+ match the corrected client API. Sandbox happened to tolerate the
20
+ missing param and return an empty page, but a real tenant would 400.
21
+
22
+ ### Updated
23
+
24
+ - Peer dependency `@mcp-abap-adt/calm-client` bumped from `^0.1.0` to
25
+ `^0.2.0`. Required for the corrected method signatures.
26
+
27
+ ### Tests
28
+
29
+ - Unit tests for the two affected bonus tools rewritten to assert
30
+ `INVALID_ARGUMENT` without `projectId` and positional-`projectId`
31
+ forwarding to the client. 225 passed (was 224), 7 skipped, 1 todo.
32
+
33
+ ## 0.2.0 — 2026-05-13
34
+
35
+ Parity with the consetto-Rust port plus a wider bonus surface, live
36
+ integration coverage, and a logger that is safe to use under MCP
37
+ stdio. **54 tools** (was 23), **232 tests** (was 103), full build
38
+ green.
39
+
40
+ ### Added
41
+
42
+ - **Tasks CRUD (M7)** — `create`, `update`, `delete`, `create_comment`.
43
+ - **TestCases CRUD (M8)** — `create`, `update`, `delete`, plus nested
44
+ `create_activity` / `create_action`.
45
+ - **Documents CRUD (M9)** — `create`, `update`, `delete`.
46
+ - **Hierarchy CRUD (M10)** — `create_node`, `update_node`,
47
+ `delete_node`.
48
+ - **Feature external references (M11)** —
49
+ `create_external_reference`, `delete_external_reference`.
50
+ - **Projects writes (M12)** — `create`.
51
+ - **Logs writes (M13)** — `post` (OpenTelemetry-style record
52
+ ingestion).
53
+ - **Bonus read tools (M16, +12)** — surface every read method already
54
+ in `calm-client` that consetto-Rust does not expose:
55
+ - `documents.list_statuses`, `documents.list_types`
56
+ - `projects.list_programs`, `projects.get_program`,
57
+ `projects.list_team_members`, `projects.list_timeboxes`
58
+ - `features.list_external_references`
59
+ - `tasks.list_deliverables`, `tasks.list_workstreams`,
60
+ `tasks.list_references`
61
+ - `testCases.list_activities`, `testCases.list_actions`
62
+ - **Integration test suite (M14)** under
63
+ `src/__tests__/integration/*.sandbox.test.ts` — one file per service,
64
+ gated on env, runs against the SAP sandbox in CI.
65
+ - **Live OAuth2 gates (M17)** —
66
+ `describeWhenLive`, `describeOAuth2`, `describeMutating` alongside
67
+ the existing sandbox gates. Mutation tests are opt-in via
68
+ `CALM_ALLOW_MUTATIONS=1` and always finalise via
69
+ `try/finally { delete }`.
70
+ - **`StderrLogger` (M15)** — minimal `ILogger` that routes every level
71
+ to stderr. Wired into `runStdio` so the bin emits lifecycle events
72
+ (start, transport bound, shutdown) without ever touching stdout
73
+ (which MCP-stdio reserves for the JSON-RPC frame stream). The
74
+ family's `PinoLogger` / `DefaultLogger` write `info`/`debug` to
75
+ stdout by default — using either inside a stdio MCP server would
76
+ corrupt every call.
77
+ - **`scripts/smoke-sandbox.mjs`** — 30-second smoke check: spawns the
78
+ stdio bin, lists tools, exercises a handful of read endpoints,
79
+ exits non-zero on any unexpected failure.
80
+
81
+ ### Fixed
82
+
83
+ - `list_hierarchy` and `list_test_cases` no longer ship sandbox-
84
+ incompatible properties in their default `$select`. The sandbox
85
+ OData type does not expose `parentNodeUuid` / `rootNodeUuid` on
86
+ `HierarchyNodes`, nor `statusCode` on `ManualTestCases`; defaults
87
+ returned 400 against the sandbox. Removed from `DEFAULT_FIELDS`;
88
+ callers can still opt in via `fields: [...]` against a tenant that
89
+ exposes them.
90
+ - `config.loadEnv` no longer reads the cwd-level `.env` under Jest.
91
+ The config-unit suite assumes a clean env; a developer's local
92
+ sandbox `.env` was silently leaking into it. Smoke scripts and the
93
+ stdio bin load `.env` explicitly via `dotenv` themselves.
94
+
95
+ ### Notes
96
+
97
+ - All HTTP / OData work lives in `@mcp-abap-adt/calm-client`. This
98
+ package is pure tool-shim: JSON Schema, args validation, error
99
+ mapping. The 19 new mutation tools added in M7–M13 required *zero*
100
+ changes to the client.
101
+ - Mutations against the shared SAP sandbox at `api.sap.com` are
102
+ intentionally not exercised by `npm test`; reachability is verified
103
+ via each tool's `INVALID_ARGUMENT` guard. Real ingestion happens
104
+ only under `describeMutating` on an opt-in live backend.
105
+
3
106
  ## 0.1.0 — 2026-04-24
4
107
 
5
108
  First usable release. 23 MCP tools covering all 9 Cloud ALM services,
package/README.md CHANGED
@@ -4,8 +4,9 @@
4
4
 
5
5
  MCP server for **SAP Cloud ALM**, built on
6
6
  [`@mcp-abap-adt/calm-client`](https://github.com/fr0ster/mcp-calm-client).
7
- Ships 23 MCP tools covering all 9 Cloud ALM services, with rich JSON
8
- Schema descriptions that let an LLM plan multi-step workflows.
7
+ Ships **54 MCP tools** covering all 9 Cloud ALM services full CRUD
8
+ where the service supports it, plus a wide read surface — with rich
9
+ JSON Schema descriptions that let an LLM plan multi-step workflows.
9
10
 
10
11
  This package is **dual-purpose**:
11
12
 
@@ -17,9 +18,10 @@ This package is **dual-purpose**:
17
18
 
18
19
  ## Status
19
20
 
20
- **0.1.0** — 23 tools, 103 unit tests, full build green. Integration
21
- against a live Cloud ALM tenant/sandbox is driven by the host — no
22
- server-side credential storage beyond the standalone `.env`.
21
+ **0.2.0** — 54 tools, 232 tests (224 unit + integration, 7 env-gated
22
+ skips, 1 todo), full build green. Integration suite runs live against
23
+ the SAP sandbox (api.sap.com) or any OAuth2 Cloud ALM tenant with a
24
+ single `.env` switch; gates skip cleanly when no backend is wired.
23
25
 
24
26
  ## Installation
25
27
 
@@ -101,7 +103,7 @@ on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
101
103
  }
102
104
  ```
103
105
 
104
- Restart Claude Desktop; the 23 `calm_*` tools become available to the
106
+ Restart Claude Desktop; the 54 `calm_*` tools become available to the
105
107
  model.
106
108
 
107
109
  ## Library: composing into another MCP server
@@ -145,19 +147,19 @@ import { ALL_GROUPS } from '@mcp-abap-adt/calm-server/tools';
145
147
  import { CalmToolRegistry } from '@mcp-abap-adt/calm-server/registry';
146
148
  ```
147
149
 
148
- ## Tool surface (23 tools across 9 services)
150
+ ## Tool surface (54 tools across 9 services)
149
151
 
150
152
  | Service | Tools |
151
153
  |---|---|
152
- | **Features** (8) | `list`, `get`, `get_by_display_id`, `create`, `update`, `delete`, `list_statuses`, `list_priorities` |
153
- | **Documents** (2) | `list`, `get` |
154
- | **TestCases** (2) | `list`, `get` |
155
- | **Hierarchy** (2) | `list`, `get_with_children` (default `$expand=[toChildNodes, toParentNode]`) |
154
+ | **Features** (11) | `list`, `get`, `get_by_display_id`, `create`, `update`, `delete`, `create_external_reference`, `delete_external_reference`, `list_external_references`, `list_statuses`, `list_priorities` |
155
+ | **Tasks** (10) | `list`, `get`, `create`, `update`, `delete`, `list_comments`, `create_comment`, `list_references`, `list_deliverables`, `list_workstreams` |
156
+ | **TestCases** (9) | `list`, `get`, `create`, `update`, `delete`, `create_activity`, `create_action`, `list_activities`, `list_actions` |
157
+ | **Documents** (7) | `list`, `get`, `create`, `update`, `delete`, `list_statuses`, `list_types` |
158
+ | **Projects** (7) | `list`, `get`, `create`, `list_programs`, `get_program`, `list_team_members`, `list_timeboxes` |
159
+ | **Hierarchy** (5) | `list`, `get_with_children`, `create_node`, `update_node`, `delete_node` |
156
160
  | **Analytics** (2, read-only) | `query` (17 endpoints), `list_providers` (static catalog) |
161
+ | **Logs** (2, domain-specific REST) | `get` (provider + serviceId + time window), `post` (inbound log records) |
157
162
  | **ProcessMonitoring** (1, read-only) | `list_processes` |
158
- | **Tasks** (3) | `list`, `get`, `list_comments` |
159
- | **Projects** (2) | `list`, `get` |
160
- | **Logs** (1, domain-specific REST) | `get` (provider + serviceId + time window) |
161
163
 
162
164
  Every MCP tool:
163
165
  - Has a full JSON Schema with descriptions — the LLM reads these to plan.
@@ -173,11 +175,20 @@ See `src/tools/<service>/*.ts` for per-tool argument schemas.
173
175
 
174
176
  ## Destructive tools (write operations)
175
177
 
176
- Currently only **Features** exposes `create` / `update` / `delete`. For
177
- the other 8 services, destructive tools are deferred to a future minor
178
- release they require live-tenant validation before the patterns are
179
- promoted. If you need them earlier, open an issue or contribute via the
180
- per-service handler modules; pattern is identical to the Features ones.
178
+ Every Cloud ALM service that supports writes is now exposed:
179
+
180
+ - **Features**: `create`, `update`, `delete`, plus external-reference
181
+ `create` / `delete`
182
+ - **Tasks**: `create`, `update`, `delete`, `create_comment`
183
+ - **TestCases**: `create`, `update`, `delete`, plus nested
184
+ `create_activity` / `create_action`
185
+ - **Documents**: `create`, `update`, `delete`
186
+ - **Hierarchy**: `create_node`, `update_node`, `delete_node`
187
+ - **Projects**: `create`
188
+ - **Logs**: `post` (inbound OpenTelemetry-style record ingestion)
189
+
190
+ The shared SAP sandbox at `api.sap.com` is read-friendly only — mutation
191
+ integration tests are opt-in (see Live-tenant integration below).
181
192
 
182
193
  ## Debug logging
183
194
 
@@ -197,16 +208,28 @@ git clone git@github.com:fr0ster/mcp-calm-server.git
197
208
  cd mcp-calm-server
198
209
  npm install
199
210
 
200
- npm run test # 103 unit tests, no network
211
+ npm run test # 224 unit + integration tests
201
212
  npm run build # emits dist/, includes executable bin
202
213
  npm run lint:check # biome
203
214
  ```
204
215
 
205
- No integration tests in this package — the `CalmClient` peer already
206
- covers live-tenant verification (see
207
- [mcp-calm-client/docs/TESTING.md](https://github.com/fr0ster/mcp-calm-client/blob/main/docs/TESTING.md)).
208
- Tools here only mediate between MCP and the client; their contracts
209
- are fully exercised via mocks.
216
+ ### Live-tenant integration
217
+
218
+ `src/__tests__/integration/` runs against a real backend when env is
219
+ present, and skips cleanly when it isn't (so `npm test` without secrets
220
+ is always green). Five gates, drop them into `.env`:
221
+
222
+ | Gate | Env trigger | What it unlocks |
223
+ |---|---|---|
224
+ | `describeSandbox` | `CALM_MODE=sandbox` + `CALM_API_KEY` | The `api.sap.com` sandbox |
225
+ | `describeOAuth2` | `CALM_MODE=oauth2` + `CALM_BASE_URL` + 3× UAA env | Live tenant (incl. endpoints absent from the sandbox catalog, e.g. Business Processes) |
226
+ | `describeWhenLive` | either of the above | Read-side tests that work in either mode |
227
+ | `describeWithProject` | live backend + `CALM_PROJECT_ID` | Project-scoped chains (features list→get, tasks list→get→comments, …) |
228
+ | `describeMutating` | live + `CALM_PROJECT_ID` + `CALM_ALLOW_MUTATIONS=1` | Write tests (every mutation finalises via `try/finally { delete }`) |
229
+
230
+ A quick smoke script lives at `scripts/smoke-sandbox.mjs` — spawns the
231
+ stdio bin, lists tools, calls a handful of read endpoints, and exits
232
+ 1 on any non-skip failure.
210
233
 
211
234
  ## License
212
235
 
package/dist/bin/stdio.js CHANGED
@@ -2,14 +2,17 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const runStdio_1 = require("../server/runStdio");
5
+ const stderrLogger_1 = require("../server/stderrLogger");
5
6
  (0, runStdio_1.runStdio)().catch((err) => {
7
+ // runStdio() may fail before its own logger is wired (config throws,
8
+ // missing env, etc.), so the catch block keeps a separate logger.
9
+ // StderrLogger guarantees we never write to stdout — keeping the
10
+ // MCP-stdio contract intact even on startup failure.
11
+ const logger = new stderrLogger_1.StderrLogger();
6
12
  const msg = err instanceof Error ? err.message : String(err);
7
- // All diagnostic output on stderr — stdout is reserved for the MCP
8
- // protocol stream.
9
- process.stderr.write(`[calm-mcp] startup failed: ${msg}\n`);
10
- if (err instanceof Error && err.stack) {
11
- process.stderr.write(`${err.stack}\n`);
12
- }
13
+ logger.error(`[calm-mcp] startup failed: ${msg}`, {
14
+ stack: err instanceof Error ? err.stack : undefined,
15
+ });
13
16
  process.exit(1);
14
17
  });
15
18
  //# sourceMappingURL=stdio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";;;AACA,iDAA8C;AAE9C,IAAA,mBAAQ,GAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAChC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,mEAAmE;IACnE,mBAAmB;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAAC;IAC5D,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../../src/bin/stdio.ts"],"names":[],"mappings":";;;AACA,iDAA8C;AAC9C,yDAAsD;AAEtD,IAAA,mBAAQ,GAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAChC,qEAAqE;IACrE,kEAAkE;IAClE,iEAAiE;IACjE,qDAAqD;IACrD,MAAM,MAAM,GAAG,IAAI,2BAAY,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,8BAA8B,GAAG,EAAE,EAAE;QAChD,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACpD,CAAC,CAAC;IACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAYD,wBAAgB,UAAU,IAAI,iBAAiB,CAkC9C"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAU9B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAYD,wBAAgB,UAAU,IAAI,iBAAiB,CAkC9C"}
@@ -14,10 +14,16 @@ let loaded = false;
14
14
  function loadEnv() {
15
15
  if (loaded)
16
16
  return;
17
+ loaded = true;
18
+ // Under Jest, never auto-load a cwd-level .env: unit tests assert on
19
+ // the absence of CALM_* vars, and a developer's local sandbox .env
20
+ // would otherwise leak into that suite and make assertions flaky.
21
+ // Smoke scripts and the stdio bin explicitly call dotenvConfig themselves.
22
+ if (process.env.JEST_WORKER_ID)
23
+ return;
17
24
  const path = (0, node_path_1.resolve)(process.cwd(), '.env');
18
25
  if ((0, node_fs_1.existsSync)(path))
19
26
  (0, dotenv_1.config)({ path });
20
- loaded = true;
21
27
  }
22
28
  function required(name) {
23
29
  const v = process.env[name];
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":";;AAWA,0BAKC;AAwBD,gCAkCC;AA1ED,qCAAqC;AACrC,yCAAoC;AACpC,mCAAgD;AAEhD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB;;;;GAIG;AACH,SAAgB,OAAO;IACrB,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAA,oBAAU,EAAC,IAAI,CAAC;QAAE,IAAA,eAAY,EAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAcD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,6CAA6C,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,EAAE,CAAC;IACV,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAElC,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC;IAEX,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC;YAClC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC;YAC3C,eAAe,EAAE,QAAQ,CAAC,wBAAwB,CAAC;YACnD,SAAS;SACV,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,IAAI;YACJ,OAAO,EACL,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qCAAqC;YACpE,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,SAAS;SACV,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,GAAG,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":";;AAWA,0BAUC;AAwBD,gCAkCC;AA/ED,qCAAqC;AACrC,yCAAoC;AACpC,mCAAgD;AAEhD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB;;;;GAIG;AACH,SAAgB,OAAO;IACrB,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,GAAG,IAAI,CAAC;IACd,qEAAqE;IACrE,mEAAmE;IACnE,kEAAkE;IAClE,2EAA2E;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO;IACvC,MAAM,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAA,oBAAU,EAAC,IAAI,CAAC;QAAE,IAAA,eAAY,EAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAcD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,6CAA6C,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,EAAE,CAAC;IACV,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAElC,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC;IAEX,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC;YAClC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC;YAC3C,eAAe,EAAE,QAAQ,CAAC,wBAAwB,CAAC;YACnD,SAAS;SACV,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,IAAI;YACJ,OAAO,EACL,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qCAAqC;YACpE,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,SAAS;SACV,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,GAAG,CAAC,CAAC;AAC5D,CAAC"}
@@ -3,10 +3,8 @@
3
3
  * builds a `CalmClient`, wires all tool groups onto a
4
4
  * `BaseCalmMcpServer`, and binds the server to the stdio transport.
5
5
  *
6
- * Errors during config/connect phase are written to stderr so the
7
- * host (Claude Desktop / whoever) sees a meaningful failure instead
8
- * of a silent hang. On success, stdio takes over — no console output
9
- * from the server.
6
+ * Logging goes through `StderrLogger` never stdout because MCP
7
+ * stdio reserves stdout for the JSON-RPC protocol stream.
10
8
  */
11
9
  export declare function runStdio(): Promise<void>;
12
10
  //# sourceMappingURL=runStdio.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runStdio.d.ts","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B9C"}
1
+ {"version":3,"file":"runStdio.d.ts","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAwC9C"}
@@ -6,35 +6,45 @@ const tools_1 = require("../tools");
6
6
  const BaseCalmMcpServer_1 = require("./BaseCalmMcpServer");
7
7
  const buildClient_1 = require("./buildClient");
8
8
  const config_1 = require("./config");
9
+ const stderrLogger_1 = require("./stderrLogger");
9
10
  const PACKAGE_NAME = '@mcp-abap-adt/calm-server';
10
- const PACKAGE_VERSION = '0.0.1';
11
+ const PACKAGE_VERSION = '0.2.1';
11
12
  /**
12
13
  * Entry point for standalone stdio mode. Reads `.env` + env vars,
13
14
  * builds a `CalmClient`, wires all tool groups onto a
14
15
  * `BaseCalmMcpServer`, and binds the server to the stdio transport.
15
16
  *
16
- * Errors during config/connect phase are written to stderr so the
17
- * host (Claude Desktop / whoever) sees a meaningful failure instead
18
- * of a silent hang. On success, stdio takes over — no console output
19
- * from the server.
17
+ * Logging goes through `StderrLogger` never stdout because MCP
18
+ * stdio reserves stdout for the JSON-RPC protocol stream.
20
19
  */
21
20
  async function runStdio() {
21
+ const logger = new stderrLogger_1.StderrLogger();
22
22
  const config = (0, config_1.readConfig)();
23
23
  const calm = (0, buildClient_1.buildCalmClient)(config);
24
24
  const server = new BaseCalmMcpServer_1.BaseCalmMcpServer({
25
25
  name: PACKAGE_NAME,
26
26
  version: PACKAGE_VERSION,
27
27
  calm,
28
+ logger,
28
29
  groups: [...tools_1.ALL_GROUPS],
29
30
  });
31
+ logger.info('calm-mcp starting', {
32
+ package: PACKAGE_NAME,
33
+ version: PACKAGE_VERSION,
34
+ mode: config.mode,
35
+ baseUrl: config.baseUrl,
36
+ tools: server.listRegisteredTools().length,
37
+ });
30
38
  // Best-effort early token / connectivity probe so misconfiguration
31
39
  // surfaces on startup rather than on the first tool call.
32
40
  await calm.getConnection().connect();
33
41
  const transport = new stdio_js_1.StdioServerTransport();
34
42
  await server.connect(transport);
43
+ logger.info('calm-mcp transport bound (stdio)');
35
44
  // Graceful shutdown on SIGINT / SIGTERM — ensures stdio is cleanly
36
45
  // closed so the host does not see a dangling child process.
37
46
  const shutdown = async () => {
47
+ logger.info('calm-mcp shutdown signal received');
38
48
  try {
39
49
  await server.close();
40
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runStdio.js","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":";;AAmBA,4BA4BC;AA/CD,wEAAiF;AACjF,oCAAsC;AACtC,2DAAwD;AACxD,+CAAgD;AAChD,qCAAsC;AAEtC,MAAM,YAAY,GAAG,2BAA2B,CAAC;AACjD,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC;;;;;;;;;GASG;AACI,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAA,6BAAe,EAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,qCAAiB,CAAC;QACnC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,eAAe;QACxB,IAAI;QACJ,MAAM,EAAE,CAAC,GAAG,kBAAU,CAAC;KACxB,CAAC,CAAC;IAEH,mEAAmE;IACnE,0DAA0D;IAC1D,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;IAErC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"runStdio.js","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":";;AAkBA,4BAwCC;AA1DD,wEAAiF;AACjF,oCAAsC;AACtC,2DAAwD;AACxD,+CAAgD;AAChD,qCAAsC;AACtC,iDAA8C;AAE9C,MAAM,YAAY,GAAG,2BAA2B,CAAC;AACjD,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC;;;;;;;GAOG;AACI,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,IAAI,2BAAY,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAA,6BAAe,EAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,qCAAiB,CAAC;QACnC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,eAAe;QACxB,IAAI;QACJ,MAAM;QACN,MAAM,EAAE,CAAC,GAAG,kBAAU,CAAC;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC/B,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,eAAe;QACxB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,mBAAmB,EAAE,CAAC,MAAM;KAC3C,CAAC,CAAC;IAEH,mEAAmE;IACnE,0DAA0D;IAC1D,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;IAErC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAEhD,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ILogger } from '@mcp-abap-adt/interfaces';
2
+ /**
3
+ * Minimal `ILogger` that writes every level to **stderr only**.
4
+ *
5
+ * Why this exists: the MCP **stdio** transport reserves stdout for the
6
+ * JSON-RPC protocol stream. Any byte written to stdout that is not a
7
+ * valid MCP frame breaks the connection. Both the family's
8
+ * `DefaultLogger` and `PinoLogger` write `info`/`debug` to stdout by
9
+ * default — using them inside a stdio MCP server would corrupt the
10
+ * stream. This logger sidesteps that by sending every message to
11
+ * fd=2.
12
+ *
13
+ * Library consumers that wire `BaseCalmMcpServer` over a non-stdio
14
+ * transport (HTTP, custom) should bring their own logger — they don't
15
+ * have the stdout-collision concern.
16
+ */
17
+ export declare class StderrLogger implements ILogger {
18
+ private write;
19
+ info(message: string, meta?: unknown): void;
20
+ warn(message: string, meta?: unknown): void;
21
+ error(message: string, meta?: unknown): void;
22
+ debug(message: string, meta?: unknown): void;
23
+ }
24
+ //# sourceMappingURL=stderrLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stderrLogger.d.ts","sourceRoot":"","sources":["../../src/server/stderrLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAa,YAAW,OAAO;IAC1C,OAAO,CAAC,KAAK;IAQb,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAI5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;CAI7C"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StderrLogger = void 0;
4
+ /**
5
+ * Minimal `ILogger` that writes every level to **stderr only**.
6
+ *
7
+ * Why this exists: the MCP **stdio** transport reserves stdout for the
8
+ * JSON-RPC protocol stream. Any byte written to stdout that is not a
9
+ * valid MCP frame breaks the connection. Both the family's
10
+ * `DefaultLogger` and `PinoLogger` write `info`/`debug` to stdout by
11
+ * default — using them inside a stdio MCP server would corrupt the
12
+ * stream. This logger sidesteps that by sending every message to
13
+ * fd=2.
14
+ *
15
+ * Library consumers that wire `BaseCalmMcpServer` over a non-stdio
16
+ * transport (HTTP, custom) should bring their own logger — they don't
17
+ * have the stdout-collision concern.
18
+ */
19
+ class StderrLogger {
20
+ write(prefix, message, meta) {
21
+ if (meta !== undefined) {
22
+ process.stderr.write(`${prefix} ${message} ${JSON.stringify(meta)}\n`);
23
+ }
24
+ else {
25
+ process.stderr.write(`${prefix} ${message}\n`);
26
+ }
27
+ }
28
+ info(message, meta) {
29
+ this.write('[INFO]', message, meta);
30
+ }
31
+ warn(message, meta) {
32
+ this.write('[WARN]', message, meta);
33
+ }
34
+ error(message, meta) {
35
+ this.write('[ERROR]', message, meta);
36
+ }
37
+ debug(message, meta) {
38
+ if (process.env.CALM_LOG_LEVEL?.toLowerCase() !== 'debug')
39
+ return;
40
+ this.write('[DEBUG]', message, meta);
41
+ }
42
+ }
43
+ exports.StderrLogger = StderrLogger;
44
+ //# sourceMappingURL=stderrLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stderrLogger.js","sourceRoot":"","sources":["../../src/server/stderrLogger.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAa,YAAY;IACf,KAAK,CAAC,MAAc,EAAE,OAAe,EAAE,IAAc;QAC3D,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAc;QAClC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAc;QAClC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAc;QACnC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAc;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,KAAK,OAAO;YAAE,OAAO;QAClE,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;CACF;AAzBD,oCAyBC"}
@@ -0,0 +1,5 @@
1
+ import type { ICreateDocumentParams, IDocument } from '@mcp-abap-adt/calm-client';
2
+ import type { ICalmHandlerEntry } from '../../registry/types';
3
+ export type ICreateDocumentArgs = ICreateDocumentParams;
4
+ export declare const createDocumentTool: ICalmHandlerEntry<ICreateDocumentArgs, IDocument>;
5
+ //# sourceMappingURL=createDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDocument.d.ts","sourceRoot":"","sources":["../../../src/tools/documents/createDocument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,SAAS,EACV,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AA8CxD,eAAO,MAAM,kBAAkB,EAAE,iBAAiB,CAChD,mBAAmB,EACnB,SAAS,CAIV,CAAC"}
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDocumentTool = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const definition = {
6
+ name: 'calm_documents_create',
7
+ description: 'Create a new Cloud ALM document. Destructive. Requires `title`; other fields are optional. NOTE the write-side field is `typeCode` (read-side returns `documentTypeCode`). Returns the newly created document.',
8
+ inputSchema: {
9
+ type: 'object',
10
+ required: ['title'],
11
+ properties: {
12
+ title: { type: 'string', description: 'Document title.' },
13
+ content: {
14
+ type: 'string',
15
+ description: 'Optional document body (markdown or plain text).',
16
+ },
17
+ projectId: {
18
+ type: 'string',
19
+ description: 'Optional project scope.',
20
+ },
21
+ typeCode: {
22
+ type: 'string',
23
+ description: 'Document type code (write-side spelling).',
24
+ },
25
+ statusCode: { type: 'string', description: 'Optional starting status.' },
26
+ priorityCode: { type: 'string', description: 'Optional priority.' },
27
+ },
28
+ },
29
+ };
30
+ const handler = async (ctx, args) => {
31
+ if (!args?.title) {
32
+ throw new utils_1.CalmToolError({
33
+ code: 'INVALID_ARGUMENT',
34
+ message: 'title is required',
35
+ });
36
+ }
37
+ try {
38
+ return await ctx.calm.getDocuments().create(args);
39
+ }
40
+ catch (err) {
41
+ throw (0, utils_1.mapCalmErrorForTool)(err);
42
+ }
43
+ };
44
+ exports.createDocumentTool = {
45
+ toolDefinition: definition,
46
+ handler,
47
+ };
48
+ //# sourceMappingURL=createDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createDocument.js","sourceRoot":"","sources":["../../../src/tools/documents/createDocument.ts"],"names":[],"mappings":";;;AASA,uCAAiE;AAIjE,MAAM,UAAU,GAAwB;IACtC,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,gNAAgN;IAClN,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,OAAO,CAAC;QACnB,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE;YACzD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;aAChE;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,yBAAyB;aACvC;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,2CAA2C;aACzD;YACD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;YACxE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE;SACpE;KACF;CACF,CAAC;AAEF,MAAM,OAAO,GAAoD,KAAK,EACpE,GAAG,EACH,IAAI,EACJ,EAAE;IACF,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,qBAAa,CAAC;YACtB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,mBAAmB;SAC7B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAA,2BAAmB,EAAC,GAAG,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC;AAEW,QAAA,kBAAkB,GAG3B;IACF,cAAc,EAAE,UAAU;IAC1B,OAAO;CACR,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ICalmHandlerEntry } from '../../registry/types';
2
+ export interface IDeleteDocumentArgs {
3
+ uuid: string;
4
+ }
5
+ export interface IDeleteDocumentResult {
6
+ deleted: true;
7
+ uuid: string;
8
+ }
9
+ export declare const deleteDocumentTool: ICalmHandlerEntry<IDeleteDocumentArgs, IDeleteDocumentResult>;
10
+ //# sourceMappingURL=deleteDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteDocument.d.ts","sourceRoot":"","sources":["../../../src/tools/documents/deleteDocument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAElB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAiCD,eAAO,MAAM,kBAAkB,EAAE,iBAAiB,CAChD,mBAAmB,EACnB,qBAAqB,CAItB,CAAC"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteDocumentTool = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const definition = {
6
+ name: 'calm_documents_delete',
7
+ description: 'Delete a Cloud ALM document by UUID. Destructive. Returns a confirmation object; raises NOT_FOUND if the document does not exist.',
8
+ inputSchema: {
9
+ type: 'object',
10
+ required: ['uuid'],
11
+ properties: {
12
+ uuid: { type: 'string', description: 'Document UUID to delete.' },
13
+ },
14
+ },
15
+ };
16
+ const handler = async (ctx, args) => {
17
+ if (!args?.uuid) {
18
+ throw new utils_1.CalmToolError({
19
+ code: 'INVALID_ARGUMENT',
20
+ message: 'uuid is required',
21
+ });
22
+ }
23
+ try {
24
+ await ctx.calm.getDocuments().delete(args.uuid);
25
+ return { deleted: true, uuid: args.uuid };
26
+ }
27
+ catch (err) {
28
+ throw (0, utils_1.mapCalmErrorForTool)(err);
29
+ }
30
+ };
31
+ exports.deleteDocumentTool = {
32
+ toolDefinition: definition,
33
+ handler,
34
+ };
35
+ //# sourceMappingURL=deleteDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteDocument.js","sourceRoot":"","sources":["../../../src/tools/documents/deleteDocument.ts"],"names":[],"mappings":";;;AAKA,uCAAiE;AAWjE,MAAM,UAAU,GAAwB;IACtC,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACT,mIAAmI;IACrI,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClB,UAAU,EAAE;YACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0BAA0B,EAAE;SAClE;KACF;CACF,CAAC;AAEF,MAAM,OAAO,GAGT,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACtB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,qBAAa,CAAC;YACtB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,kBAAkB;SAC5B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAA,2BAAmB,EAAC,GAAG,CAAC,CAAC;IACjC,CAAC;AACH,CAAC,CAAC;AAEW,QAAA,kBAAkB,GAG3B;IACF,cAAc,EAAE,UAAU;IAC1B,OAAO;CACR,CAAC"}
@@ -1,9 +1,19 @@
1
1
  import { HandlerGroup } from '../../registry/HandlerGroup';
2
2
  import type { ICalmHandlerEntry } from '../../registry/types';
3
+ export type { ICreateDocumentArgs } from './createDocument';
4
+ export { createDocumentTool } from './createDocument';
5
+ export type { IDeleteDocumentArgs, IDeleteDocumentResult, } from './deleteDocument';
6
+ export { deleteDocumentTool } from './deleteDocument';
3
7
  export type { IGetDocumentArgs } from './getDocument';
4
8
  export { getDocumentTool } from './getDocument';
9
+ export type { IListDocumentStatusesResult } from './listDocumentStatuses';
10
+ export { listDocumentStatusesTool } from './listDocumentStatuses';
5
11
  export type { IListDocumentsArgs } from './listDocuments';
6
12
  export { listDocumentsTool } from './listDocuments';
13
+ export type { IListDocumentTypesResult } from './listDocumentTypes';
14
+ export { listDocumentTypesTool } from './listDocumentTypes';
15
+ export type { IUpdateDocumentArgs } from './updateDocument';
16
+ export { updateDocumentTool } from './updateDocument';
7
17
  export declare const DOCUMENTS_HANDLERS: readonly ICalmHandlerEntry[];
8
18
  export declare const DOCUMENTS_GROUP: HandlerGroup;
9
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/documents/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,eAAO,MAAM,kBAAkB,EAAE,SAAS,iBAAiB,EAG1D,CAAC;AAEF,eAAO,MAAM,eAAe,cAG3B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/documents/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAS9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,eAAO,MAAM,kBAAkB,EAAE,SAAS,iBAAiB,EAQ1D,CAAC;AAEF,eAAO,MAAM,eAAe,cAG3B,CAAC"}