Skip to main content
Glama
engineersonal

Panaya MCP Server

README.md
# Panaya MCP Server

A TypeScript-based Model Context Protocol (MCP) server that exposes Panaya REST APIs as MCP tools. It is designed to run over `stdio`, so it can be connected to Claude Desktop and other MCP-compatible clients.

The server discovers a fixed set of Panaya entities, registers one grouped MCP tool per entity, calls the Panaya REST API with `X-Auth-Token`, and returns responses in both Claude-readable text and machine-readable `structuredContent`.

## Features

- Runs as a local MCP server over stdio.
- Connects to Panaya REST APIs using `PANAYA_BASE_URL`, `PANAYA_USERNAME`, and `PANAYA_TOKEN`.
- Generates a short-lived Panaya API token through `POST /api/accesstoken` and sends that generated token as `X-Auth-Token`.
- Registers tools for common Panaya entities:
  - `projects`
  - `requirements`
  - `tests`
  - `defects`
  - `releases`
  - `cycles`
  - `businessProcesses`
- Provides a unified action-based tool for each entity.
- Supports `list`, `get`, `create`, `update`, `delete`, and `search` actions.
- Includes an API operation registry tool, `panaya_operations`, that can discover, describe, and call documented Panaya REST operations by `operationId`.
- Returns REST responses as formatted JSON text for Claude.
- Also returns structured JSON through MCP `structuredContent`.
- Writes server logs to `stderr`, keeping `stdout` clean for MCP protocol messages.
- Loads `.env` from the project root even when launched by Claude from another working directory.

## Project Structure

```text
src/
  client/
    panayaClient.ts       # Panaya REST client
  config/
    env.ts                # Environment loading and validation
  discovery/
    apiDiscovery.ts       # Entity discovery and metadata lookup
  generator/
    schemaBuilder.ts      # Metadata schema helper retained for future typed schemas
    toolGenerator.ts      # Registers grouped MCP tools for each entity
  generated/
    panayaOperations.ts   # Generated API operation registry
  runtime/
    bootstrap.ts          # Discovers entities and registers tools
  index.ts                # stdio MCP server entry point
  logger.ts               # stderr logger for Claude MCP logs
  server.ts               # MCP server factory
```

## Prerequisites

- Node.js 20 or newer is recommended.
- npm
- A valid Panaya API token.
- Claude Desktop, if you want to connect this server to Claude.

## Setup

Install dependencies:

```powershell
npm.cmd install
```

Create a local `.env` file from the example:

```powershell
Copy-Item .env.example .env
```

Edit `.env`:

```env
PANAYA_BASE_URL=https://your-panaya-host.example.com
PANAYA_USERNAME=your_panaya_username@example.com
PANAYA_TOKEN=your_long_lived_panaya_access_token_here
```

`PANAYA_TOKEN` should be the long-lived access token used by Panaya's `/api/accesstoken` endpoint. The MCP server exchanges it for a generated API token and uses the generated token in `X-Auth-Token`.

Do not commit `.env`. It contains credentials and is ignored by Git.

Build the TypeScript project:

```powershell
npm.cmd run build
```

## Running Locally

Start the compiled MCP server:

```powershell
npm.cmd start
```

This server is intended for MCP stdio clients. When run directly in a terminal, it will wait for MCP JSON-RPC messages on stdin.

## Claude Desktop Configuration

Open your Claude Desktop config file:

```text
C:\Users\sonal_sharma\AppData\Roaming\Claude\claude_desktop_config.json
```

Add this server inside the existing `mcpServers` object:

```json
{
  "mcpServers": {
    "panaya-mcp-sonal": {
      "command": "node",
      "args": [
        "C:\\Users\\sonal_sharma\\panaya-mcp-sonal\\dist\\index.js"
      ],
      "cwd": "C:\\Users\\sonal_sharma\\panaya-mcp-sonal"
    }
  }
}
```

If you already have other MCP servers configured, keep them and add only the `panaya-mcp-sonal` entry.

After editing the config:

1. Run `npm.cmd run build`.
2. Fully restart Claude Desktop.
3. Ask Claude to use the Panaya MCP server, for example:

```text
Use the Panaya MCP server to list projects.
```

## Available Tools

For each entity, the server registers one grouped tool:

```text
panaya_<entity>
```

Current tools:

```text
panaya_projects
panaya_requirements
panaya_tests
panaya_defects
panaya_releases
panaya_cycles
panaya_businessProcesses
```

The server also registers this operation registry tool:

```text
panaya_auth
panaya_operations
```

Use `panaya_auth` with `{ "action": "refresh" }` if a long Claude session needs to force-refresh the generated Panaya API token before continuing.

`panaya_operations` is generated from the official Panaya Postman collection. The current generated registry contains 146 curated API examples from `Shared Panaya Catalog of API examples.postman_collection.json`. Swagger can still be used as a fallback source if the Postman collection is unavailable.

Each tool accepts this input shape:

```json
{
  "action": "list",
  "id": "optional-id",
  "operationId": "optional-operation-id",
  "projectId": "optional-project-id",
  "pathParams": {},
  "queryParams": {},
  "data": {},
  "query": {}
}
```

Only `action` is required. Valid actions are:

```text
list
get
create
update
delete
search
operation
```

Action field requirements:

| Action | Required fields | REST call |
| --- | --- | --- |
| `list` | none | `GET /api/v1/<entity>` |
| `list` with project filter | `projectId` | `GET /api/v1/<entity>?projectId=<projectId>` |
| `get` | `id` | `GET /api/v1/<entity>/<id>` |
| `create` | `data` | `POST /api/v1/<entity>` |
| `update` | `id`, `data` | `PUT /api/v1/<entity>/<id>` |
| `delete` | `id` | `DELETE /api/v1/<entity>/<id>` |
| `search` | `query` | `POST /api/v1/<entity>/search` |
| `operation` | `operationId`, plus required `pathParams`, `queryParams`, and `data` | Calls the matching documented operation |

### Tool Examples

List all projects:

```json
{
  "action": "list"
}
```

List requirements for a project:

```json
{
  "action": "list",
  "projectId": "19051"
}
```

Get one project:

```json
{
  "action": "get",
  "id": "19051"
}
```

Search an entity:

```json
{
  "action": "search",
  "query": {
    "status": "ACTIVE"
  }
}
```

Create an entity:

```json
{
  "action": "create",
  "data": {
    "name": "Example"
  }
}
```

Update an entity:

```json
{
  "action": "update",
  "id": "19051",
  "data": {
    "name": "Updated Example"
  }
}
```

Delete an entity:

```json
{
  "action": "delete",
  "id": "19051"
}
```

Call a documented operation from a grouped entity tool:

```json
{
  "action": "operation",
  "operationId": "Create_Defect",
  "pathParams": {
    "projectId": "19051"
  },
  "data": {
    "name": "Example defect"
  }
}
```

### Operation Registry Tool

Use `panaya_operations` to discover, describe, or call documented REST operations that do not fit the simple grouped entity actions.

List operations:

```json
{
  "action": "list",
  "search": "defect",
  "limit": 10
}
```

Filter by operation tag/folder:

```json
{
  "action": "list",
  "tag": "Defects",
  "limit": 25
}
```

Describe one operation:

```json
{
  "action": "describe",
  "operationId": "Create_Defect"
}
```

Call one operation:

```json
{
  "action": "call",
  "operationId": "Get_All_Defects",
  "pathParams": {
    "projectId": "19051"
  },
  "queryParams": {
    "pageSize": 50,
    "pageNumber": 1
  }
}
```

Call one paginated page:

```json
{
  "action": "call",
  "operationId": "Get_All_Defects",
  "pathParams": {
    "projectId": "19051"
  },
  "queryParams": {
    "pageSize": 100,
    "pageNumber": 1
  }
}
```

Fetch all pages for APIs that use `pageNumber` and `pageSize`:

```json
{
  "action": "call",
  "operationId": "Get_All_Defects",
  "pathParams": {
    "projectId": "19051"
  },
  "pagination": {
    "all": true,
    "pageSize": 100,
    "maxPages": 20
  }
}
```

For APIs that use different parameter names, override them:

```json
{
  "action": "call",
  "operationId": "Some_Paginated_Operation",
  "pathParams": {
    "projectId": "19051"
  },
  "pagination": {
    "all": true,
    "pageParam": "page",
    "pageSizeParam": "size",
    "startPage": 0,
    "pageSize": 100,
    "maxPages": 20
  }
}
```

For operations with a request body, include `data`:

```json
{
  "action": "call",
  "operationId": "Create_Defect",
  "pathParams": {
    "projectId": "19051"
  },
  "data": {
    "name": "Example"
  }
}
```

### Auth Tool

Force-refresh the generated Panaya API token:

```json
{
  "action": "refresh"
}
```

## Response Format

Tool responses include a text block for Claude:

```json
{
  "content": [
    {
      "type": "text",
      "text": "[{\"projectId\":123,\"projectName\":\"Example\"}]"
    }
  ]
}
```

They also include structured JSON for MCP clients that support it:

```json
{
  "structuredContent": {
    "data": [
      {
        "projectId": 123,
        "projectName": "Example"
      }
    ]
  }
}
```

Array responses are wrapped as `{ "data": [...] }` because MCP `structuredContent` must be an object.

## Logging

Logs are written to `stderr`, which Claude Desktop shows in the MCP server logs. This keeps `stdout` reserved for MCP protocol traffic.

Example log lines:

```text
[panaya-mcp-sonal] 2026-07-05T11:09:42.590Z tool:start {"name":"panaya_projects","action":"list"}
[panaya-mcp-sonal] 2026-07-05T11:09:42.590Z GET {"path":"/api/v1/projects"}
[panaya-mcp-sonal] 2026-07-05T11:09:42.658Z tool:success {"name":"panaya_projects","action":"list","resultType":"array","count":11}
```

The logger intentionally avoids printing tokens, headers, or request bodies.

## Security Notes

- Never commit `.env`.
- Keep `PANAYA_TOKEN` private.
- Keep `PANAYA_USERNAME` private if it identifies a real user account.
- Rotate the token immediately if it is accidentally shared.
- Review any create, update, or delete tool usage before allowing Claude or another MCP client to call it.
- Avoid logging request bodies if they may contain sensitive business data.
- Keep `node_modules/` and `dist/` out of Git; rebuild from source after cloning.

## Development Commands

Build:

```powershell
npm.cmd run build
```

Run compiled server:

```powershell
npm.cmd start
```

Run TypeScript source directly:

```powershell
npm.cmd run dev
```

Regenerate the operation registry from the official Panaya Postman collection:

```powershell
npm.cmd run generate:operations:postman -- "C:\Users\sonal_sharma\Desktop\Shared Panaya Catalog of API examples.postman_collection.json"
npm.cmd run build
```

If only Swagger metadata is available, regenerate from Swagger instead:

```powershell
npm.cmd run generate:operations:swagger -- C:\Users\sonal_sharma\Desktop\Panaya_Swagger.json
npm.cmd run build
```

Note: On some Windows systems, PowerShell blocks `npm.ps1`. Use `npm.cmd` if you see an execution policy error.

## Troubleshooting

### Claude Shows `Invalid URL`

This usually means `PANAYA_BASE_URL` was not loaded. Confirm that `.env` exists in the project root and contains:

```env
PANAYA_BASE_URL=https://your-panaya-host.example.com
PANAYA_USERNAME=your_panaya_username@example.com
PANAYA_TOKEN=your_long_lived_panaya_access_token_here
```

Then rebuild and restart Claude Desktop.

### Panaya Returns 401

Check that `PANAYA_USERNAME` and `PANAYA_TOKEN` are valid for the configured Panaya host. The server first calls:

```text
POST /api/accesstoken
```

Then it sends the generated token using:

```text
X-Auth-Token: <token>
```

For long-running Claude sessions, call `panaya_auth` with `{ "action": "refresh" }` and then retry the failed request.

### Claude Does Not Show the Server

Check that:

- `npm.cmd run build` completed successfully.
- `dist/index.js` exists.
- `claude_desktop_config.json` points to the correct absolute path.
- Claude Desktop was fully restarted after config changes.

### Logs Do Not Appear

Claude only captures MCP server logs from `stderr`. This project logs with `console.error()` through `src/logger.ts`.

## Git Hygiene

This repository includes a `.gitignore` that excludes:

- `.env` and other secret files
- `node_modules/`
- `dist/`
- logs and temporary files
- local IDE/tool state

Before pushing to GitHub, verify that secrets are not staged:

```powershell
git status
```

Only commit source, configuration templates, lockfiles, and documentation.

TDQS

C2.4/5.0

Scored across 9 tools

Disambiguation2/5

The generic panaya_operations tool overlaps with all resource-specific tools, as it can execute any operation. This makes it unclear whether to use a specific tool or the generic one, especially since the 'unified' tools likely wrap similar operations.

Naming Consistency4/5

Most tools follow a consistent panaya_<resource> pattern with snake_case. However, panaya_businessProcesses uses camelCase, and panaya_operations is not a resource but a catch-all, so it deviates slightly from the naming convention.

Tool Count4/5

With 9 tools, the count is reasonable for a domain-specific server. The inclusion of a generic operations tool alongside resource-specific tools is somewhat redundant but does not make the count inappropriate.

Completeness4/5

The resource-specific tools cover core entities, and panaya_operations fills any gaps by allowing arbitrary operations. Minor gaps may exist in the specific tools (e.g., no explicit update/delete exposed), but the generic tool provides a workaround.

Maintenance

ActivityStale
ResponsivenessNo issues