Skip to main content
Glama

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

calm_list

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 $filter/$select/$expand/$orderby/$top/$skip; REST resources accept contextual params (project_id, task_id, task_type, timebox_id/timebox_name, …). fields projects the response on any resource; count_only/group_by return a live count instead of the records.

calm_get

Fetch a single entity by id (a feature also by display id, e.g. 6-123).

calm_analytics

Query an analytics provider (Defects, Tasks, Tests, …). Providers span the whole tenant, so count_only/group_by here answer "how many across all projects". It aggregates but does not sort: $orderby is silently ignored by the service, so it is not offered.

calm_resources

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>" })                 // -> details
  • Open 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

count_only: true

Returns only the total. One $count request on OData and analytics; on REST resources calmcp pages and keeps just the tally.

group_by: "status"

Returns { total, groups: [{ value, count }] } instead of records. Accepts several fields ("projectName,priority"). Doubles as a way to discover the values a field actually takes.

group_limit

Caps the number of groups (default 50); the tail folds into otherCount rather than being dropped.

count: true

Returns the total alongside the records (OData and analytics only).

Which tool you call decides where the number comes from:

  • calm_analytics counts tenant-wide, with no project_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 carry unit: "entities". A provider whose identity and measure calmcp does not know is counted by rows and labelled unit: "rows" with a warning.

  • calm_list counts live, within whatever the resource is scoped to (resource: "tasks" needs a project_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

CALM_SANDBOX

true to use the SAP Business Accelerator Hub sandbox with CALM_API_KEY.

CALM_API_KEY

Sandbox API key (sandbox mode).

CALM_TENANT, CALM_REGION

Tenant subdomain and region (e.g. eu10) for OAuth2 mode.

CALM_CLIENT_ID, CALM_CLIENT_SECRET

OAuth2 client-credentials from the service binding.

CALM_DESTINATION_NAME

Name of a bound BTP Destination (BTP mode; takes precedence).

PORT, CALM_CORS_ORIGINS

HTTP transport port and allowed CORS origins.

CALM_DEBUG, CALM_TIMEOUT_SECONDS

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.

  1. Download the latest calmcp-<version>.mcpb from the Releases page (or build it locally — see below).

  2. Double-click it, or open Claude Desktop → Settings → Extensions and drag the file in.

  3. 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.

  4. 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 root

This 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.json snippet 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      # biome

Run over stdio (local MCP clients)

CALM_SANDBOX=true CALM_API_KEY=<key> node dist/index.js

Example 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/mcp

Deploy 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:

  1. Cloud Foundry CLI — install for your OS (macOS, Windows, Linux) per the official guide.

  2. 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 multiapps fails with bad 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.json

This 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 push

After 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 (default SAP_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 of CALM_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 CALM_DESTINATION_NAME (default SAP_CALM)

Type

HTTP

Proxy Type

Internet

URL

your Cloud ALM API base including the /api suffix: https://<tenant>.<region>.alm.cloud.sap/api (the region-only form https://<region>.alm.cloud.sap/api works too)

Authentication

OAuth2ClientCredentials

Client ID

clientid from the service key

Client Secret

clientsecret from the service key

Token Service URL

the service key's url plus /oauth/token: https://<tenant>.authentication.<region>.hana.ondemand.com/oauth/token

Token Service URL Type

Dedicated

Notes:

  • The /api suffix 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

CALM_PUBLIC_URL

Public base URL used in OAuth metadata and the callback. Defaults to the first route in VCAP_APPLICATION, so it's normally not needed.

CALM_DCR_SIGNING_SECRET

Secret for HMAC-signing dynamic client registrations. Set it (e.g. cf set-env calmcp-srv CALM_DCR_SIGNING_SECRET "$(openssl rand -base64 48)") so registered clients survive a cf deploy (which rotates the XSUAA clientsecret). Defaults to the XSUAA clientsecret.

CALM_HTTP_API_KEY

Shared secret for the alternative API-key path. Generate with openssl rand -base64 48. Leave empty to rely on XSUAA only. See the Copilot Studio guide.

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>/mcp

  • Claude Desktop: Settings → Connectors → add a custom connector with the /mcp URL.

  • Cursor / VS Code / others: add an MCP server of type HTTP (Streamable) at the /mcp URL.

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 HTTP

Pushes 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 / major

The 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, or npm run lint:fix to 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 tools
calm_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".

ParametersJSON Schema
NameRequiredDescriptionDefault
topNoOData $top — maximum number of records
skipNoOData $skip — records to skip
filterNoOData $filter, e.g. "status eq 'CIPDFCTOPEN'"
selectNoOData $select — comma-separated field list
orderbyNoOData $orderby, e.g. "priority desc" (OData resources / analytics only)
providerYesAnalytics provider (e.g. Defects, Tasks, Tests). Supports $orderby.

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesEntity id (uuid, REST id, or feature display id like "6-123")
expandNoOData $expand for OData entities
resourceYesWhich single entity to fetch (see calm_resources)

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idsNoFetch specific tasks by id (resource:tasks); sent as a comma-separated list
topNoOData $top — maximum number of records
skipNoOData $skip — records to skip
tagsNoTag filters (resource:tasks)
limitNoREST page size (REST resources)
expandNoOData $expand — comma-separated navigation properties
fieldsNoComma-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
filterNoOData $filter, e.g. "status eq 'CIPDFCTOPEN'"
offsetNoREST page offset (REST resources)
selectNoOData $select — comma-separated field list
statusNoStatus code filter (e.g. CIPDFCTOPEN; or deployment plan status)
filtersNoFree-form REST filters for landscape_objects / bsm_events
orderbyNoOData $orderby, e.g. "priority desc" (OData resources / analytics only)
task_idNoTask id (required for task sub-resources)
team_idNoTeam id (required for team_roles/program_team_roles)
resourceYesWhich collection to list (see calm_resources for the catalog and required params)
task_typeNoTask type filter (resource:tasks). CALMDEF = Defect
program_idNoProgram id (required for program_teams)
project_idNoProject id (required for tasks/deliverables/etc.)
sub_statusNoSub-status code filter (resource:tasks)
timebox_idNoTimebox (sprint/phase) id filter (resource:tasks). Applied by calmcp after fetching, paging through the project automatically
assignee_idNoAssignee id filter (resource:tasks)
timebox_nameNoTimebox 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_dateNoLast-changed date filter (resource:tasks). Prefix with an operator: gt:, eq: or lt:, e.g. "gt:2026-08-01"
solution_process_idNoSolution process id filter (resource:task_solution_process_assignments)
last_changed_timestampNoLast-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

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicNoOptional: a resource/provider name, or "recipes" for worked examples

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 2 tool updatesv0.2.0
    • Changedcalm_get1 field changed
      • changedInput schema / properties / resource / enum
        Previous 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"
        +]
    • Changedcalm_list10 fields changed
      • addedInput schema / properties / fields
        Added 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"
        +}
      • addedInput schema / properties / ids
        Added value: +{
        +  "description": "Fetch specific tasks by id (resource:tasks); sent as a comma-separated list",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / last_changed_date
        Added value: +{
        +  "description": "Last-changed date filter (resource:tasks). Prefix with an operator: gt:, eq: or lt:, e.g. \"gt:2026-08-01\"",
        +  "type": "string"
        +}
      • addedInput schema / properties / last_changed_timestamp
        Added 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"
        +}
      • addedInput schema / properties / program_id
        Added value: +{
        +  "description": "Program id (required for program_teams)",
        +  "type": "string"
        +}
      • changedInput schema / properties / resource / enum
        Previous 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"
        +]
      • addedInput schema / properties / solution_process_id
        Added value: +{
        +  "description": "Solution process id filter (resource:task_solution_process_assignments)",
        +  "type": "string"
        +}
      • changedInput schema / properties / team_id / description
        Previous value: -"Team id (required for team_roles)"New value: +"Team id (required for team_roles/program_team_roles)"
      • addedInput schema / properties / timebox_id
        Added value: +{
        +  "description": "Timebox (sprint/phase) id filter (resource:tasks). Applied by calmcp after fetching, paging through the project automatically",
        +  "type": "string"
        +}
      • addedInput schema / properties / timebox_name
        Added 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"
        +}
  2. 4 tool updatesv0.1.0
    • First observedcalm_analytics
    • First observedcalm_get
    • First observedcalm_list
    • First observedcalm_resources

TDQS

A4.3/5.0
Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness5/5

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

ActivityMaintained
ResponsivenessNo issues

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

Related MCP Servers

Latest Blog Posts

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