Skip to content
This repository was archived by the owner on Jul 29, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Tools and Workflow

- Use `fd` instead of `find`. Use `rg` instead of `grep`. Fallback to the originals only if the new tools fail for whatever reason.

## Common Development Commands

- Build stdio version: `bun run build`
- Build HTTP version: `bun run build:http`
- Build both versions: `bun run build:all`
- Run tests: `bun test`
- Watch tests: `bun test:watch`
- Run a single test file: `bun test src/services/__tests__/jira-api.test.ts`
- Development mode: `bun run dev`
- Start stdio server: `bun start` (requires build first)
- Start HTTP server: `bun start:http` (requires build:http first)

## Architecture Overview

This is a Model Context Protocol (MCP) server that provides access to JIRA data with:

- Support for both Jira Cloud and Jira Server/Data Center instances
- Relationship tracking between issues (mentions, links, parent/child, epics)
- Optimized data payloads for AI context windows
- Rich content cleaning and transformation

### Key Components

1. **Entry Points**:
- `src/index.ts` - Stdio transport for local use (default)
- `src/http-server.ts` - HTTP transport with streaming support for remote access

2. **Transport Layer**:
- `StdioServerTransport` - For local CLI integration
- `StreamableHTTPServerTransport` - For HTTP with SSE streaming support
- Automatic protocol selection based on client needs (JSON or SSE)

3. **Service Layer**:
- `JiraApiService` - Handles Jira Cloud API v3 interactions
- `JiraServerApiService` - Handles Jira Server/Data Center API v2 interactions
- Both support Basic and Bearer authentication methods

4. **Type System** (`src/types/jira.ts`):
- Clean data models optimized for AI consumption
- Atlassian Document Format (ADF) handling
- Relationship tracking interfaces

## Implementation Details

### Authentication

- **Basic Auth**: Used with API tokens (Cloud) or username/password (Server)
- **Bearer Auth**: Used with Personal Access Tokens (Server/DC 8.14.0+)
- Auth type is determined by `JIRA_AUTH_TYPE` environment variable

### Data Processing

- Extracts text from Atlassian Document Format
- Tracks issue mentions in descriptions and comments
- Deduplicates and categorizes relationships
- Removes unnecessary metadata for efficient context usage

### API Limits

- Search results: Maximum 50 issues per request
- Epic children: Maximum 100 issues per request
- File attachments: Multipart form data with Base64 encoding

### Error Handling

- Comprehensive error messages with HTTP status codes
- Specific JIRA API error parsing
- Network error detection
- Input validation for all tool parameters

## Environment Variables

### JIRA Configuration (Required)

- `JIRA_API_TOKEN` - API token or password
- `JIRA_BASE_URL` - Instance URL
- `JIRA_USER_EMAIL` - User email

### JIRA Options

- `JIRA_TYPE` - 'cloud' (default) or 'server'
- `JIRA_AUTH_TYPE` - 'basic' (default) or 'bearer'

### HTTP Server Options

- `MCP_PORT` - HTTP server port (default: 3000)
- `MCP_ENABLE_SESSIONS` - Enable session management (default: true)
- `MCP_JSON_ONLY` - Force JSON-only responses, no SSE (default: false)

### HTTP Authentication Options

#### Simple Bearer Token

- `MCP_AUTH_TOKEN` - Static bearer token for basic authentication

#### OAuth2 Authentication (Recommended for production)

- `MCP_OAUTH_ENABLED` - Enable OAuth2 authentication mode
- `MCP_OAUTH_ISSUER` - OAuth2 authorization server URL
- `MCP_OAUTH_INTROSPECTION_URL` - Token introspection endpoint
- `MCP_OAUTH_CLIENT_ID` - Client ID for introspection (if required)
- `MCP_OAUTH_CLIENT_SECRET` - Client secret for introspection (if required)
- `MCP_OAUTH_REQUIRED_SCOPE` - Required OAuth2 scope (optional)
- `MCP_OAUTH_DOCS_URL` - Documentation URL for OAuth metadata (optional)

## HTTP Transport Details

The HTTP server uses Bun's native `Bun.serve()` API to avoid additional dependencies. It implements the MCP Streamable HTTP specification:

### Endpoints

- **POST /mcp** - Handles JSON-RPC messages
- **GET /mcp** - Opens SSE stream for server-initiated messages
- **DELETE /mcp** - Terminates session
- **GET /.well-known/oauth-protected-resource** - OAuth2 metadata (when OAuth2 enabled)

### HTTP Authentication

The server supports two authentication modes:

1. **Simple Bearer Token**: Set `MCP_AUTH_TOKEN` for static token authentication
2. **OAuth2**: Full OAuth2 bearer token authentication with token introspection

When OAuth2 is enabled:

- Tokens are validated via the configured introspection endpoint
- Optional scope enforcement with `MCP_OAUTH_REQUIRED_SCOPE`
- Standard OAuth2 error responses (401/403 with WWW-Authenticate headers)
- OAuth2 Protected Resource Metadata endpoint for discovery

### Response Modes

The transport automatically chooses between JSON responses and SSE streaming based on the communication needs. Session management allows multiple concurrent clients with isolated state.

### Security Notes

- CORS is enabled for browser-based clients
- Always use HTTPS in production (configure reverse proxy)
- OAuth2 is recommended for multi-user deployments
- See `docs/oauth2-setup.md` for detailed OAuth2 configuration
124 changes: 123 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The server will automatically use the correct API version and authentication met
- Clean and transform rich JIRA content for AI context efficiency
- Support for file attachments with secure multipart upload handling
- **Supports both Jira Cloud and Jira Server (Data Center) APIs**
- **Streaming HTTP transport** for remote access with session management

## Prerequisites

Expand All @@ -37,6 +38,8 @@ The server will automatically use the correct API version and authentication met

## Environment Variables

### JIRA Configuration

```bash
JIRA_API_TOKEN=your_api_token # API token for Cloud, PAT or password for Server/DC
JIRA_BASE_URL=your_jira_instance_url # e.g., https://your-domain.atlassian.net
Expand All @@ -45,6 +48,27 @@ JIRA_TYPE=cloud # 'cloud' or 'server' (optional, defaul
JIRA_AUTH_TYPE=basic # 'basic' or 'bearer' (optional, defaults to 'basic')
```

### HTTP Server Configuration (Optional)

```bash
# Basic settings
MCP_PORT=3000 # HTTP server port (default: 3000)
MCP_ENABLE_SESSIONS=true # Enable session management (default: true)
MCP_JSON_ONLY=false # Force JSON-only responses (default: false)

# Authentication - Option 1: Simple Bearer Token
MCP_AUTH_TOKEN=your-secret-token # Simple bearer token authentication

# Authentication - Option 2: OAuth2 (Recommended for production)
MCP_OAUTH_ENABLED=true # Enable OAuth2 authentication
MCP_OAUTH_ISSUER=https://oauth.provider # OAuth2 authorization server URL
MCP_OAUTH_INTROSPECTION_URL=https://... # Token introspection endpoint
MCP_OAUTH_CLIENT_ID=client-id # Client ID for introspection (if required)
MCP_OAUTH_CLIENT_SECRET=client-secret # Client secret for introspection (if required)
MCP_OAUTH_REQUIRED_SCOPE=mcp:tools # Required OAuth2 scope (optional)
MCP_OAUTH_DOCS_URL=https://... # Documentation URL for OAuth metadata (optional)
```

### Authentication Methods

- **Jira Cloud**: Use API tokens with Basic authentication
Expand Down Expand Up @@ -72,11 +96,15 @@ cd jira-mcp

```bash
bun install
bun run build
bun run build:all # Builds both stdio and HTTP versions
```

### 3. Configure the MCP server

You can run the MCP server in two modes:

#### Stdio Mode (Default - for local use)

Edit the appropriate configuration file:

**macOS:**
Expand Down Expand Up @@ -114,6 +142,100 @@ Add the following configuration under the `mcpServers` object:
}
```

#### HTTP Mode (for remote access)

The HTTP mode allows remote access to the MCP server with streaming support:

**1. Set environment variables:**

```bash
# Required JIRA configuration (same as stdio mode)
export JIRA_API_TOKEN="your_api_token"
export JIRA_BASE_URL="your_jira_instance_url"
export JIRA_USER_EMAIL="your_email"

# HTTP server configuration
export MCP_PORT=3000 # Server port (default: 3000)
export MCP_AUTH_TOKEN="your-secret" # Optional: Bearer token for authentication
export MCP_ENABLE_SESSIONS=true # Enable session management (default: true)
export MCP_JSON_ONLY=false # Force JSON-only responses (default: false)
```

**2. Start the HTTP server:**

```bash
bun run start:http
```

**3. Connect your MCP client to the HTTP endpoint:**

```json
{
"mcpServers": {
"jira-http": {
"url": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer your-secret" // If MCP_AUTH_TOKEN is set
}
}
}
}
```

**Security Options:**

##### Option 1: Simple Bearer Token (Quick Setup)

Set `MCP_AUTH_TOKEN` environment variable and include the token in requests:

```json
{
"headers": {
"Authorization": "Bearer your-secret-token"
}
}
```

##### Option 2: OAuth2 Authentication (Recommended for Production)

The server supports standard OAuth2 bearer token authentication with any OAuth2 provider:

```bash
# Enable OAuth2 mode
export MCP_OAUTH_ENABLED=true

# Configure OAuth2 provider
export MCP_OAUTH_ISSUER="https://your-oauth-provider.com"
export MCP_OAUTH_INTROSPECTION_URL="https://your-oauth-provider.com/oauth2/introspect"

# Optional: Client credentials for introspection endpoint
export MCP_OAUTH_CLIENT_ID="your-client-id"
export MCP_OAUTH_CLIENT_SECRET="your-client-secret"

# Optional: Required scope
export MCP_OAUTH_REQUIRED_SCOPE="mcp:tools"
```

**OAuth2 Flow:**

1. Users obtain an access token from your OAuth2 provider
2. Include the token in requests: `Authorization: Bearer <access_token>`
3. The MCP server validates tokens via the introspection endpoint
4. OAuth metadata available at: `/.well-known/oauth-protected-resource`

**Popular OAuth2 Providers:**

- **Auth0**: Set `MCP_OAUTH_INTROSPECTION_URL` to `https://YOUR_DOMAIN.auth0.com/oauth/introspect`
- **Keycloak**: Set to `https://YOUR_KEYCLOAK/realms/YOUR_REALM/protocol/openid-connect/token/introspect`
- **Okta**: Set to `https://YOUR_OKTA_DOMAIN/oauth2/v1/introspect`

**Security Best Practices:**

- Always use HTTPS in production (use a reverse proxy like nginx)
- Configure firewall rules to restrict access
- Use OAuth2 for multi-user scenarios
- Implement rate limiting at the proxy level

### 4. Restart the MCP server

Within Cline's MCP settings, restart the MCP server. Restart Claude Desktop to load the new MCP server.
Expand Down
10 changes: 7 additions & 3 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@

"@microsoft/tsdoc-config": ["@microsoft/tsdoc-config@0.17.1", "", { "dependencies": { "@microsoft/tsdoc": "0.15.1", "ajv": "~8.12.0", "jju": "~1.4.0", "resolve": "~1.22.2" } }, "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw=="],

"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.16.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-8ofX7gkZcLj9H9rSd50mCgm3SSF8C7XoclxJuLoV0Cz3rEQ1tv9MZRYYvJtm9n1BiEQQMzSmE/w2AEkNacLYfg=="],
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.17.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-qFfbWFA7r1Sd8D697L7GkTd36yqDuTkvz0KfOGkgXR8EUhQn3/EDNIR/qUdQNMT8IjmasBvHWuXeisxtXTQT2g=="],

"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],

Expand Down Expand Up @@ -358,7 +358,7 @@

"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],

"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],

"pkce-challenge": ["pkce-challenge@5.0.0", "", {}, "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ=="],

Expand Down Expand Up @@ -446,7 +446,7 @@

"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],

"vite": ["vite@7.0.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.2", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw=="],
"vite": ["vite@7.0.6", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.6", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.40.0", "tinyglobby": "^0.2.14" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg=="],

"vite-plugin-dts": ["vite-plugin-dts@4.5.4", "", { "dependencies": { "@microsoft/api-extractor": "^7.50.1", "@rollup/pluginutils": "^5.1.4", "@volar/typescript": "^2.4.11", "@vue/language-core": "2.2.0", "compare-versions": "^6.1.1", "debug": "^4.4.0", "kolorist": "^1.8.0", "local-pkg": "^1.0.0", "magic-string": "^0.30.17" }, "peerDependencies": { "typescript": "*", "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg=="],

Expand All @@ -466,6 +466,8 @@

"@microsoft/tsdoc-config/ajv": ["ajv@8.12.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA=="],

"@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],

"@rushstack/node-core-library/ajv": ["ajv@8.13.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.4.1" } }, "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA=="],

"@vue/language-core/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
Expand All @@ -476,6 +478,8 @@

"mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],

"tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],

"@microsoft/tsdoc-config/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],

"@rushstack/node-core-library/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
Expand Down
Loading