calmcp
calmcp is a read-only MCP server that lets AI assistants query SAP Cloud ALM data through four tools.
calm_list – List or query any Cloud ALM collection: tasks (requirements, user stories, defects), projects, features, documents, test cases, hierarchy, landscape, code lists, and more; supports OData filters, field projection, grouping, counting, and sprint/timebox filters.
calm_get – Fetch a single entity by id, e.g. a task, project, feature, document, test case, or program team, including features by display id like
6-123.calm_analytics – Run tenant-wide analytics queries across providers (Defects, Tasks, Tests, Features, Projects, etc.) with filters, sorting, counts, and group-by breakdowns.
calm_resources – Discover valid resources, providers, required parameters, code lists, and worked recipes for using the other tools.
Read-only by design – Never creates, updates, or deletes data in SAP Cloud ALM.
Response size controls – Project fields, count-only queries, group_by summaries, and payload caps keep large results manageable.
Flexible deployment – Runs locally over stdio, remotely over Streamable HTTP, or on SAP BTP Cloud Foundry with OAuth2, destination-based auth, and MCP-native OAuth for AI clients.
Provides read-only access to SAP Cloud ALM resources including tasks, projects, features, documents, test cases, analytics, and more through consolidated tools.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@calmcpList open defects ordered by priority"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
calmcp - Cloud ALM MCP Server
A read-only Model Context Protocol (MCP) server that bridges AI assistants (Claude, GitHub Copilot, …) to SAP Cloud ALM (aka CALM). It exposes the Cloud ALM read APIs through four consolidated, intent-based tools, runs over stdio locally or Streamable HTTP remotely, and deploys to SAP BTP Cloud Foundry.
calmcp is read-only: it never creates, updates or deletes data in SAP Cloud ALM.
calmcp is my second SAP Cloud ALM MCP bridge . It succeeds an earlier
Rust implementation sap-cloud-alm-mcp and reuses the knowledge of the Cloud ALM APIs, while taking a different technical direction.
The Architecture is based on marianfoo's arc-1 as reference architecutre. See docs/PREDECESSOR.md for the project's lineage.
Tools
Tool | Purpose |
| List/query any collection — tasks (incl. requirements, user stories and defects), projects, features, documents, test cases, hierarchy nodes, cross-library objects, landscape objects, status events, code lists. OData resources accept |
| Fetch a single entity by id (a feature also by display id, e.g. |
| Query an analytics provider ( |
| Discovery: the catalog of resources/providers, per-provider analytics dimensions and measures, the task type/status/priority code lists, and worked recipes. |
Worked examples
How many user stories are there in the tenant?
calm_analytics({ "provider": "Tasks", "filter": "type eq 'User Story'", "count_only": true })Open defects per project
calm_analytics({ "provider": "Defects", "filter": "defectStatus eq 'CIPDFCTOPEN'", "group_by": "projectName" })All open defects ordered by priority (sort the records yourself; nothing in Cloud ALM sorts these, and analytics ignores
$orderby)calm_list({ "resource": "tasks", "project_id": "<uuid>", "task_type": "CALMDEF", "status": "CIPDFCTOPEN", "fields": "displayId,title,priority,assigneeName" })Assigned features for defect
Y(two steps)calm_list({ "resource": "task_feature_assignments", "task_id": "Y" }) // -> featureIds calm_get({ "resource": "feature", "id": "<featureId>" }) // -> detailsOpen user stories in a sprint
calm_list({ "resource": "tasks", "project_id": "<uuid>", "task_type": "CALMUS", "status": "CIPUSOPEN", "timebox_name": "Sprint 5", "fields": "displayId,title,status,assigneeName,dueDate" })
Call calm_resources (optionally { "topic": "recipes" }) at any time to discover valid
resource/provider values and required parameters.
Keeping responses small
A task carries 67 attributes, most of them null, and the Tasks REST endpoint supports neither
$select nor a timebox filter. Unprojected task lists therefore run to hundreds of KB and overflow
agent hosts such as Microsoft Copilot Studio. calm_list adds two options, both applied by calmcp
after fetching: fields projects the records, and timebox_id/timebox_name selects one sprint
(paging through the project so the filter is complete). Unknown field names and unknown timebox
names are rejected rather than silently ignored.
calmcp also caps the response itself. A payload over CALM_MAX_RESPONSE_BYTES (default 100 KB) is
withheld and replaced by a summary naming how many records matched, which fields they carry, and
how to ask again. Handing the payload over instead means the client truncates the JSON mid-record
and the model answers from a fragment, which reads as authoritative and is wrong.
Counting and breakdowns
Never answer "how many?" by listing records and counting them. Both query tools take:
Option | Effect |
| Returns only the total. One |
| Returns |
| Caps the number of groups (default 50); the tail folds into |
| Returns the total alongside the records (OData and analytics only). |
Which tool you call decides where the number comes from:
calm_analyticscounts tenant-wide, with noproject_id. It reads a daily snapshot, so the result says so. An analytics row is a data point rather than a record: the service emits one row per combination of a record's dimension values, so one task with several tags and workstreams can produce a hundred rows. Counting rows would overstate the answer badly, so calmcp instead selects the provider's count measure and lets the service aggregate, or counts distinct record ids for a grand total. Results carryunit: "entities". A provider whose identity and measure calmcp does not know is counted by rows and labelledunit: "rows"with a warning.calm_listcounts live, within whatever the resource is scoped to (resource: "tasks"needs aproject_id).
The analytics service drops a $filter on a field it does not support instead of rejecting it,
and then answers with every row, so a wrong field name yields a confident count of the wrong thing.
A filtered analytics count therefore also counts the same window unfiltered, and sets
filterVerified: false when the two totals match. That detects a dropped filter but cannot prove
one was applied, so prefer group_by on the field you would have filtered: it sends no filter, so
nothing can be dropped, and it returns every value with its count in one call.
Every counting result reports method, the effective filter, and complete. A walk stopped by
the 20 000-record page cap comes back with complete: false and says the real total is higher,
rather than presenting a floor as the answer.
Covered services
Tasks, Projects (incl. programs and program teams), Features, Documents, Process Hierarchy, Process Scopes, Custom Processes, Test Management (manual + automated), Test Plans (BETA), Analytics, BSM/Status Events, Landscape, and Cross-Library (Applications, Configurations, Developments, Interfaces).
In Cloud ALM the Tasks service is not just to-dos: requirements, user stories, defects,
sub-tasks, roadmap and project tasks, quality gates, checklist items and risks are all tasks,
distinguished by a type code. So resource: "tasks" with task_type: "CALMREQU", "CALMUS" or
"CALMDEF" is how you list requirements, user stories or defects. Call calm_resources for the
full code list.
All services are on API version v1. For the spec revision behind each one, and how to refresh
them, see docs/API_VERSIONS.md.
Related MCP server: @mcp-abap-adt/calm-server
Configuration
Configuration is read from environment variables (see .env.example). Two local
auth modes, plus a BTP destination mode:
Variable | Description |
|
|
| Sandbox API key (sandbox mode). |
| Tenant subdomain and region (e.g. |
| OAuth2 client-credentials from the service binding. |
| Name of a bound BTP Destination (BTP mode; takes precedence). |
| HTTP transport port and allowed CORS origins. |
| Verbose tracing and request timeout. |
Install in Claude Desktop — one-click (.mcpb)
The simplest path for a single developer: install calmcp as a Claude Desktop extension.
Download the latest
calmcp-<version>.mcpbfrom the Releases page (or build it locally — see below).Double-click it, or open Claude Desktop → Settings → Extensions and drag the file in.
Claude prompts for your Cloud ALM connection. Tenant, Region, Client ID and Client Secret are required (the secret is stored in your OS keychain). To use the SAP Business Accelerator Hub sandbox instead, turn on Use Sandbox and supply a Sandbox API Key. Debug Logging and Request Timeout are optional. Fill them in and enable the extension.
Ask Claude: "Using the SAP Cloud ALM tools, list the open defects ordered by priority." — it should call
calm_analytics.
What the bundle is: a pure-JS, cross-platform (macOS / Windows / Linux) build of the stdio server packaged with its dependencies — calmcp has no native modules, so one bundle runs everywhere. It is read-only like the rest of calmcp. For multi-user, HTTP, or BTP deployments, use the Docker image or deploy to Cloud Foundry instead (see below).
Build the bundle locally
npm run build:mcpb # → calmcp-<version>.mcpb in the repo rootThis compiles dist/, installs production dependencies, then validates and packs the bundle with
the pinned @anthropic-ai/mcpb CLI. The form Claude Desktop
shows is defined in mcpb-manifest.json; keep its version in sync with
package.json (the build fails if they differ).
Prefer to hand-edit JSON? Use the
claude_desktop_config.jsonsnippet under Run over stdio — that path also supports sandbox-only setups.
Local development
npm install
npm run build
npm test # unit tests (mocked HTTP)
npm run lint # biomeRun over stdio (local MCP clients)
CALM_SANDBOX=true CALM_API_KEY=<key> node dist/index.jsExample client config (Claude Desktop):
{
"mcpServers": {
"calmcp": {
"command": "node",
"args": ["/absolute/path/to/calmcp/dist/index.js"],
"env": { "CALM_SANDBOX": "true", "CALM_API_KEY": "<key>" }
}
}
}Run over HTTP
CALM_SANDBOX=true CALM_API_KEY=<key> PORT=8080 node dist/index.js --http
curl http://localhost:8080/health
# MCP endpoint: POST http://localhost:8080/mcpDeploy to SAP BTP Cloud Foundry
calmcp authenticates to Cloud ALM via a bound Destination (type OAuth2 client-credentials,
URL = your Cloud ALM API base, e.g. https://<tenant>.<region>.alm.cloud.sap/api). Set
CALM_DESTINATION_NAME to that destination's name.
Prerequisites
The deployment needs the Cloud Foundry CLI, the MultiApps plugin (which provides cf deploy), and
the MTA build tool:
Cloud Foundry CLI — install for your OS (macOS, Windows, Linux) per the official guide.
MultiApps plugin and MTA build tool (cross-platform):
cf install-plugin multiapps -f # registers the `cf deploy` command used below npm install --global mbt # MTA build tool cf login -a https://api.cf.<region>.hana.ondemand.com --sso # then pick the org/space
If
cf install-plugin multiappsfails withbad CPU type/ architecture errors (e.g. on arm64 machines), download the matching binary for your platform from the MultiApps releases and install it from file:cf install-plugin <downloaded-binary> -f.
Using the MTA descriptor
mbt build
cf deploy mta_archives/calmcp_<version>.mtar # <version> is the one in package.jsonThis creates and binds calmcp-xsuaa (XSUAA), calmcp-destination (Destination) and
calmcp-logs (Application Logs), and runs the HTTP transport with a /health check.
Bump the version before deploying a new build — see Releasing. Redeploying different
code under the version already running leaves cf deploy reporting the same number for both, so
there is no way to tell afterwards which build a space is on.
Using cf push
Create the services, build, then push (see manifest.yml):
cf create-service xsuaa application calmcp-xsuaa -c xs-security.json
cf create-service destination lite calmcp-destination
cf create-service application-logs lite calmcp-logs
npm run build
cf pushAfter deploy, assign the CALMCP_Viewer role collection to authorized users and create the
destination as described below.
Configure the destination
Two differently-named things are involved — don't confuse them:
calmcp-destination— the destination service instance created and bound by the deploy (mta.yaml / manifest.yml). It is the container that holds destinations; you do not edit it by hand.CALM_DESTINATION_NAME(defaultSAP_CALM) — the name of the destination entry the app looks up at runtime. This is the name you give the destination you create. It must match the value ofCALM_DESTINATION_NAME; change one and change the other.
Create the entry either at the subaccount level (Connectivity → Destinations) or inside the
calmcp-destination service instance — the Cloud SDK checks both. Fill it in from your Cloud ALM
API service key (an OAuth2 client-credentials key for the Cloud ALM API):
Field | Value |
Name | the value of |
Type |
|
Proxy Type |
|
URL | your Cloud ALM API base including the |
Authentication |
|
Client ID |
|
Client Secret |
|
Token Service URL | the service key's |
Token Service URL Type |
|
Notes:
The
/apisuffix on the URL is required — calmcp appends per-service paths (e.g./calm-features/v1) directly to this URL.The destination's Check Connection button may report 401/403; that is expected for an unauthenticated probe. The real check is the deployed app calling a tool.
No additional destination properties are needed.
Endpoint authentication (HTTP transport)
The destination above is how calmcp authenticates to Cloud ALM. Separately, the /mcp endpoint
itself is protected so only authorized callers can reach it.
Standard approach: a BTP user signing in from an AI tool. When the calmcp-xsuaa service is
bound, /mcp requires a valid XSUAA token carrying the Viewer scope (granted via the
CALMCP_Viewer role collection). calmcp detects the bound service from VCAP_SERVICES and enables
this automatically. It also exposes MCP-native OAuth (RFC 8414 discovery + RFC 7591 dynamic client
registration, proxied to XSUAA), so an AI tool such as Claude Desktop, Cursor or VS Code signs the
user in interactively with no manual token handling. The OAuth flow is delegated to XSUAA; calmcp
never sees the user's password. This is the recommended path: each user authenticates as themselves
with a BTP user and the CALMCP_Viewer role collection.
Alternative: a static API key for non-interactive, server-to-server callers (for example
Microsoft Copilot Studio). Set CALM_HTTP_API_KEY and the caller sends Authorization: Bearer <key>.
This authenticates the caller, not a user. Both methods coexist on the one endpoint. See
Connecting calmcp to Microsoft Copilot Studio.
Locally, with neither configured, /mcp is left open for development and a warning is logged. Do
not expose an unauthenticated instance publicly.
Relevant environment variables (HTTP transport):
Variable | Description |
| Public base URL used in OAuth metadata and the callback. Defaults to the first route in |
| Secret for HMAC-signing dynamic client registrations. Set it (e.g. |
| Shared secret for the alternative API-key path. Generate with |
Consuming the deployed server from an AI tool
This is the standard way to use the deployed server. /mcp speaks the Streamable HTTP MCP
transport, so point a remote-MCP-capable client at it:
Claude Code:
claude mcp add --transport http calmcp https://<route>/mcpClaude Desktop: Settings → Connectors → add a custom connector with the
/mcpURL.Cursor / VS Code / others: add an MCP server of type HTTP (Streamable) at the
/mcpURL.
On first connect the client triggers the OAuth login; sign in with your BTP user, which must hold
the CALMCP_Viewer role collection. The four tools (calm_list, calm_get, calm_analytics,
calm_resources) then appear.
For non-interactive server-to-server callers such as Microsoft Copilot Studio, use the API-key path instead: see Connecting calmcp to Microsoft Copilot Studio.
Testing
npm test # unit (mocked HTTP via undici MockAgent)
npm run test:integration # live sandbox/destination — skipped without credentials
npm run build && npm run test:e2e # real MCP calls over stdio and HTTPPushes to main and every pull request run npm ci, the version check, lint, unit tests and the
build on Node 22 and 24 (.github/workflows/ci.yml). npm ci installs
strictly from the lockfile, so a stale local node_modules can never be mistaken for a real
failure again.
Releasing
package.json is the single source of truth for the version. Four other files carry a copy —
mcpb-manifest.json, mta.yaml, src/server.ts (advertised to MCP clients) and the .mtar
filename — so bump them together, never by hand:
npm version patch # or minor / majorThe version lifecycle script runs scripts/sync-version.mjs, which
rewrites the copies and stages them; npm then makes the commit and the tag. npm run version:check
verifies the files agree and runs in CI, so drift fails the build rather than reaching a deploy.
Bump before every deploy to a shared space. The MTA version is what cf deploy reports and what
names the archive; reusing it for different code makes deploys indistinguishable after the fact.
License
MIT
Contributing
Contributions are welcome! Please ensure your code:
Builds without errors (
npm run build)Passes all tests (
npm test)Passes linting and formatting (
npm run lint, ornpm run lint:fixto auto-fix)
Disclaimer
This software is provided "as is", without warranty of any kind, express or implied.
No Responsibility
The author(s) and contributor(s) of this tool assume no responsibility or liability for any damages, losses, or consequences that may result from the use or misuse of this software. This includes, but is not limited to:
Any kind of data loss
Any damage to systems, networks, or data
Any legal consequences resulting from unauthorized or improper use
Any business losses or operational disruptions
Any security incidents or breaches
Available Tools
4 toolscalm_analyticsQuery SAP Cloud ALM analyticsA
Query an SAP Cloud ALM analytics provider (Defects, Tasks, Tests, Features, Projects, Metrics, ...). Supports $filter and $orderby — use this for sorted/aggregated questions such as "open defects ordered by priority".
| Name | Required | Description | Default |
|---|---|---|---|
| top | No | OData $top — maximum number of records | |
| skip | No | OData $skip — records to skip | |
| filter | No | OData $filter, e.g. "status eq 'CIPDFCTOPEN'" | |
| select | No | OData $select — comma-separated field list | |
| orderby | No | OData $orderby, e.g. "priority desc" (OData resources / analytics only) | |
| provider | Yes | Analytics provider (e.g. Defects, Tasks, Tests). Supports $orderby. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden. It discloses OData capabilities ($filter, $orderby) and an example, which is useful. But it does not mention whether the operation is read-only, return format, pagination behavior, or any side effects. This is a clear gap, so a 3 is appropriate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no redundancy. It front-loads the core function and quickly moves to a helpful example. Every sentence earns its place, and the parenthetical provider list is compact but valuable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a query tool with a rich schema (6 params, all described) and no output schema, the description is fairly complete. It gives a clear use case and mentions OData capabilities. It could arguably mention pagination or output, but the schema covers top/skip, and the example implies return of matching records. This is slightly above minimum viable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds a usage example that demonstrates filter/orderby semantics, but it mostly repeats what the schema already documents. It doesn't significantly deepen understanding of parameters beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function with a specific verb ('Query') and resource ('SAP Cloud ALM analytics provider'), and lists example provider types. It also hints at distinguishing from siblings via sorted/aggregated questions, and the example 'open defects ordered by priority' makes the purpose concrete.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'use this for sorted/aggregated questions', giving a clear context. However, it does not name alternative tools like calm_list or calm_get to explicitly contrast when not to use this tool, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calm_getGet one SAP Cloud ALM entityA
Fetch a single SAP Cloud ALM entity by id (a feature can also be fetched by display id like "6-123"). Choose a "resource" and pass its "id". See calm_resources for valid ones.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Entity id (uuid, REST id, or feature display id like "6-123") | |
| expand | No | OData $expand for OData entities | |
| resource | Yes | Which single entity to fetch (see calm_resources) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It accurately describes the operation as a fetch (read-only) and mentions a special case for feature display ids. No destructive or side effects are implied, and the behavior is straightforward. The description is transparent about what the tool does.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no wasted words. The first sentence states the purpose and a key detail (display id), the second gives the usage pattern. Front-loaded with essential information. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has 3 parameters, no output schema, and no annotations, the description covers the core functionality and usage. It explains how to identify the entity (id with display id option) and which resource to use. It does not describe the output format, but for a simple fetch tool, the return (the entity object) is implicit. The description is adequately complete for an agent to select and invoke the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (all parameters have descriptions). The description adds value beyond the schema by illustrating the usage pattern ('Choose a resource and pass its id') and providing an example of a feature display id. It also reinforces the resource enum hint by referencing calm_resources. This helps an agent understand how to compose the call correctly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool fetches a single entity by id, specifies the verb 'Fetch', and gives an example for features. It distinguishes from siblings like calm_list (which lists multiple) and calm_resources (which provides resource list). The title and description align well.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage guidance: 'Choose a resource and pass its id' and references calm_resources for valid resources. It also notes that features can be fetched by display id. While it does not explicitly state when not to use the tool or name alternatives, the context is sufficient for an agent to decide when to invoke this tool versus siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calm_listList SAP Cloud ALM dataA
List or query any SAP Cloud ALM collection (tasks, projects, features, documents, test cases, hierarchy nodes, cross-library objects, landscape objects, status events, code lists). Choose a "resource"; OData resources accept $filter/$select/$expand/$orderby/$top/$skip, REST resources accept contextual params. Defects: resource="tasks", task_type="CALMDEF". See calm_resources for the full catalog.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Fetch specific tasks by id (resource:tasks); sent as a comma-separated list | |
| top | No | OData $top — maximum number of records | |
| skip | No | OData $skip — records to skip | |
| tags | No | Tag filters (resource:tasks) | |
| limit | No | REST page size (REST resources) | |
| expand | No | OData $expand — comma-separated navigation properties | |
| fields | No | Comma-separated field projection applied by calmcp to the returned records (any resource). Use it to keep responses small, e.g. "displayId,title,status,assigneeName,timeboxId". Unlike $select this works for REST resources too. Unknown names are rejected | |
| filter | No | OData $filter, e.g. "status eq 'CIPDFCTOPEN'" | |
| offset | No | REST page offset (REST resources) | |
| select | No | OData $select — comma-separated field list | |
| status | No | Status code filter (e.g. CIPDFCTOPEN; or deployment plan status) | |
| filters | No | Free-form REST filters for landscape_objects / bsm_events | |
| orderby | No | OData $orderby, e.g. "priority desc" (OData resources / analytics only) | |
| task_id | No | Task id (required for task sub-resources) | |
| team_id | No | Team id (required for team_roles/program_team_roles) | |
| resource | Yes | Which collection to list (see calm_resources for the catalog and required params) | |
| task_type | No | Task type filter (resource:tasks). CALMDEF = Defect | |
| program_id | No | Program id (required for program_teams) | |
| project_id | No | Project id (required for tasks/deliverables/etc.) | |
| sub_status | No | Sub-status code filter (resource:tasks) | |
| timebox_id | No | Timebox (sprint/phase) id filter (resource:tasks). Applied by calmcp after fetching, paging through the project automatically | |
| assignee_id | No | Assignee id filter (resource:tasks) | |
| timebox_name | No | Timebox name filter, e.g. "Sprint 5" (resource:tasks). Resolved against the project's timeboxes; errors listing the known names when it does not match | |
| last_changed_date | No | Last-changed date filter (resource:tasks). Prefix with an operator: gt:, eq: or lt:, e.g. "gt:2026-08-01" | |
| solution_process_id | No | Solution process id filter (resource:task_solution_process_assignments) | |
| last_changed_timestamp | No | Last-changed timestamp filter (resource:tasks). Prefix with gt:, eq: or lt: and use ISO 8601, e.g. "gt:2026-08-01T00:00:00Z". Use this for incremental "what changed since" queries |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does disclose that OData resources accept standard query parameters while REST resources do not, and that the 'fields' parameter works for any resource by applying a projection after fetching. However, it does not mention important behavioral aspects like pagination behavior beyond OData $top/$skip, or side effects (e.g., whether listing affects system state). The description adequately covers query mechanics but not broader behavioral traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is extremely concise at three short sentences. Every sentence adds unique value: the first states scope, the second distinguishes resource types, and the third gives a concrete use case. It is front-loaded with the essential information about what the tool does.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 26 parameters, 1 required, complex OData vs REST distinction, and no output schema, the description provides a good high-level overview. It covers the key architectural choice (OData vs REST) and gives a defect-specific example. However, given the absence of an output schema and the complexity of the tool, a brief mention of what the returned records look like (e.g., typical fields per resource type) would improve completeness, though the reference to calm_resources partially compensates.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, meaning all 26 parameters already have descriptions in the JSON schema. The tool description itself does not add new semantic information about parameters—it only mentions the 'fields' parameter's advantage over $select and gives a usage example for parameters like 'timebox_name'. Since the schema already describes each parameter sufficiently, the description adds some value but does not significantly enhance understanding beyond what is in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that this tool lists or queries any SAP Cloud ALM collection, naming over a dozen specific resource types. It explicitly distinguishes its scope from siblings by referencing calm_resources for the full catalog. The verb 'list or query' is specific and actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear guidance on when to use this tool by differentiating between OData resources (which accept $filter/$select etc.) and REST resources (which accept contextual params). It also gives a concrete example for listing defects (resource='tasks', task_type='CALMDEF'). However, it lacks explicit 'when not to use' guidance relative to siblings like calm_get or calm_analytics, which would improve the score further.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
calm_resourcesDiscover SAP Cloud ALM resourcesA
Discovery helper: lists every resource/provider the other tools accept, their required parameters, the task type/status/priority code lists, and worked recipes. Pass topic="recipes" for multi-step examples, or a resource/provider name to focus.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | Optional: a resource/provider name, or "recipes" for worked examples |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description bears the full burden of disclosing behavior. It discloses that the tool is read-only in effect (it 'lists' information), enumerates the content categories (resources, parameters, code lists, recipes), and explains the topic parameter's effect. It does not detail output format or edge cases, but for a discovery helper this is acceptable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the tool's role ('Discovery helper'), and every clause adds information about the returned content or usage. No redundant words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (one optional parameter, no output schema), the description sufficiently covers its purpose, content, and filtering. Sibling tool names provide context that this is one of several Calm tools, and the description explains how this helper supports them.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents topic thoroughly with 100% coverage ('Optional: a resource/provider name, or "recipes" for worked examples'). The description repeats this instruction without adding new semantic detail, so it adds no value beyond the schema. Baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool as a 'Discovery helper' that lists resources, parameters, code lists, and recipes accepted by sibling tools. It distinguishes itself from calm_list/get/analytics by positioning itself as a meta-tool for learning about the other tools, so the purpose is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides direct usage instructions: pass topic='recipes' for multi-step examples or a resource/provider name to focus. This clearly states when to use the tool and how to tailor the query, though it doesn't explicitly name alternatives or exclusions beyond the implicit contrast with 'the other tools'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
2 tool updates
v0.2.0- Changed
calm_get1 field changed- changed
Input schema / properties / resource / enumPrevious value: -[ - "feature", - "document", - "hierarchy_node", - "manual_test_case", - "automated_test_case", - "xlib_application", - "xlib_configuration", - "xlib_development", - "xlib_interface", - "task", - "deliverable", - "project", - "program", - "timebox", - "team", - "deployment_plan", - "system_group" -]New value: +[ + "feature", + "document", + "hierarchy_node", + "manual_test_case", + "automated_test_case", + "xlib_application", + "xlib_configuration", + "xlib_development", + "xlib_interface", + "task", + "deliverable", + "project", + "program", + "timebox", + "team", + "deployment_plan", + "system_group", + "program_team", + "scope", + "solution_scenario_version", + "business_process", + "solution_process", + "solution_activity", + "process_asset", + "test_plan", + "test_case_assignment" +]
- Changed
calm_list10 fields changed- added
Input schema / properties / fieldsAdded value: +{ + "description": "Comma-separated field projection applied by calmcp to the returned records (any resource). Use it to keep responses small, e.g. \"displayId,title,status,assigneeName,timeboxId\". Unlike $select this works for REST resources too. Unknown names are rejected", + "type": "string" +} - added
Input schema / properties / idsAdded value: +{ + "description": "Fetch specific tasks by id (resource:tasks); sent as a comma-separated list", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / last_changed_dateAdded value: +{ + "description": "Last-changed date filter (resource:tasks). Prefix with an operator: gt:, eq: or lt:, e.g. \"gt:2026-08-01\"", + "type": "string" +} - added
Input schema / properties / last_changed_timestampAdded value: +{ + "description": "Last-changed timestamp filter (resource:tasks). Prefix with gt:, eq: or lt: and use ISO 8601, e.g. \"gt:2026-08-01T00:00:00Z\". Use this for incremental \"what changed since\" queries", + "type": "string" +} - added
Input schema / properties / program_idAdded value: +{ + "description": "Program id (required for program_teams)", + "type": "string" +} - changed
Input schema / properties / resource / enumPrevious value: -[ - "features", - "feature_external_references", - "feature_url_references", - "feature_task_assignments", - "feature_priorities", - "feature_statuses", - "documents", - "document_types", - "document_statuses", - "document_sources", - "document_priorities", - "document_approval_states", - "hierarchy_nodes", - "manual_test_cases", - "automated_test_cases", - "test_activities", - "test_actions", - "xlib_applications", - "xlib_configurations", - "xlib_developments", - "xlib_interfaces", - "tasks", - "task_subtasks", - "task_comments", - "task_references", - "task_relations", - "task_feature_assignments", - "task_document_assignments", - "task_hierarchy_assignments", - "deliverables", - "workstreams", - "projects", - "project_timeboxes", - "project_teams", - "team_roles", - "programs", - "system_groups", - "deployment_plans", - "landscape_objects", - "bsm_events" -]New value: +[ + "features", + "feature_external_references", + "feature_url_references", + "feature_task_assignments", + "feature_priorities", + "feature_statuses", + "documents", + "document_types", + "document_statuses", + "document_sources", + "document_priorities", + "document_approval_states", + "hierarchy_nodes", + "manual_test_cases", + "automated_test_cases", + "test_activities", + "test_actions", + "xlib_applications", + "xlib_configurations", + "xlib_developments", + "xlib_interfaces", + "xlib_application_url_references", + "xlib_configuration_url_references", + "xlib_development_url_references", + "xlib_interface_url_references", + "xlib_configuration_activities", + "xlib_configuration_activity_types", + "xlib_configuration_assignments", + "scopes", + "solution_scenario_versions", + "scope_solution_processes", + "business_processes", + "solution_processes", + "solution_process_flows", + "solution_activities", + "process_assets", + "test_plans", + "test_case_assignments", + "test_plan_tag_assignments", + "tasks", + "task_solution_process_assignments", + "task_subtasks", + "task_comments", + "task_references", + "task_relations", + "task_feature_assignments", + "task_document_assignments", + "task_hierarchy_assignments", + "deliverables", + "workstreams", + "projects", + "project_timeboxes", + "project_teams", + "team_roles", + "programs", + "program_teams", + "program_team_roles", + "system_groups", + "deployment_plans", + "landscape_objects", + "bsm_events" +] - added
Input schema / properties / solution_process_idAdded value: +{ + "description": "Solution process id filter (resource:task_solution_process_assignments)", + "type": "string" +} - changed
Input schema / properties / team_id / descriptionPrevious value: -"Team id (required for team_roles)"New value: +"Team id (required for team_roles/program_team_roles)" - added
Input schema / properties / timebox_idAdded value: +{ + "description": "Timebox (sprint/phase) id filter (resource:tasks). Applied by calmcp after fetching, paging through the project automatically", + "type": "string" +} - added
Input schema / properties / timebox_nameAdded value: +{ + "description": "Timebox name filter, e.g. \"Sprint 5\" (resource:tasks). Resolved against the project's timeboxes; errors listing the known names when it does not match", + "type": "string" +}
4 tool updates
v0.1.0- First observed
calm_analytics - First observed
calm_get - First observed
calm_list - First observed
calm_resources
TDQS
Each tool has a clearly distinct purpose: calm_list queries collections, calm_get fetches a single entity, calm_analytics handles aggregated analytics, and calm_resources provides metadata and discovery. No overlap or ambiguity.
All tools start with the 'calm_' prefix, making them easily identifiable. The second part mixes verbs (list, get) and nouns (analytics, resources), but the pattern is predictable and readable.
With 4 tools, the server is well-scoped for its purpose. Each tool earns its place—covering collection queries, single entity retrieval, analytics, and resource discovery—without being too few or too many.
For a read-only query interface, the tool set is complete. It covers listing, fetching, analytics, and self-documentation (calm_resources). There are no obvious gaps given the stated domain.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.
Related MCP Servers
- FlicenseBqualityNot gradedmaintenanceAn MCP server that enables AI assistants to interact with SAP systems via the ABAP Development Tools (ADT) REST API. It allows users to read ABAP source code, inspect DDIC objects, and execute SQL queries directly.66-
- AlicenseNot gradedqualityCmaintenanceMCP server for SAP Cloud ALM, providing 54 tools across 9 services to manage features, tasks, test cases, documents, projects, and more via natural language.285MIT
- AlicenseNot gradedqualityDmaintenanceA config-driven MCP server that exposes OData and REST APIs as MCP tools, enabling AI assistants to query, manage, and monitor SAP backends through natural language.7928MIT
- FlicenseNot gradedqualityBmaintenanceAn MCP server for AI assistants to create and manage SAP Solution Manager Focused Build Requirements and navigate the Solution Documentation process hierarchy via SAP OData API.-
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/consetto/calmcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server