Content
<!-- Generated by https://github.com/flowgent-labs/mcpfather -->
# jira-mcp
[](https://go.dev/)
[](https://modelcontextprotocol.io/)
[](https://prometheus.io/)
[](https://opentelemetry.io/)
[](https://openid.net/connect/)
[](https://ldap.com/)
[](https://helm.sh/)
> An Enterprise-grade MCP server, specifically designed for AI to call Jira Software Data Center REST API Reference APIs, generated from MCPFather — exposes **435 native tools**. Each API operation becomes an AI-callable tool with typed schemas and customized aggregate virtual tools, auth forwarding, and observability built-in, and even use as regular CLI.
## Quick Start
```sh
make
# Or with OpenTelemetry distributed tracing (gRPC or HTTP)
make build-with-otel-grpc # OTLP gRPC exporter
make build-with-otel-http # OTLP HTTP/protobuf exporter
```
### Start in `HTTP` mode (example)
```sh
# Configure your upstream API
export MCP__UPSTREAM__ENDPOINT=https://api.example.com
export MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN=your-token
./bin/jira-mcp --transport http --port 8080 &
```
- Inspection MCP frontend Authentication (OAuth2.1).
```bash
export MCP__AUTH__FRONTEND__OIDC__ENABLED=true
export MCP__AUTH__FRONTEND__OIDC__ISSUER="https://keycloak.example.com/realms/master"
export MCP__AUTH__FRONTEND__OIDC__AUDIENCE="jira-mcp"
./bin/jira-mcp -v 10 --transport http --port 8080
# Obtain the OAuth2.1 metadata.
curl -s https://iam.example.com/realms/master/.well-known/openid-configuration
curl -s localhost:8080/.well-known/oauth-protected-resource
#{"resource":"jira-mcp","authorization_servers":["https://keycloak.example.com/realms/master"],"bearer_methods_supported":["header"]}
```
### Run in `CLI` mode (example)
```sh
./bin/jira-mcp -v 1 -t cli list
./bin/jira-mcp -v 1 -t cli <tool-name> [OPTIONS]
```
## Options
| Flag | Default | Description |
|------|---------|-------------|
| `-t, --transport` | `stdio` | `stdio` / `http` / `cli` |
| `-p, --port` | `8080` | HTTP server port |
| `--metrics-port` | `0` | Override mgmt port (0 = use config) |
| `-v, --verbose` | `0` | Log verbosity (0–10) |
| `--print-default-config` | — | Print default config.yaml to stdout |
## Authentication
Auth is split into two independent layers — neither shares credentials with the other:
| Layer | Config YAML path | Env var prefix | Role |
|---|---|---|---|
| **Frontend** | `auth.frontend.oidc` | `MCP__AUTH__FRONTEND__OIDC__*` | Validates AI agent bearer tokens (inbound) |
| **Backend OIDC** | `auth.backend.oidc` | `MCP__AUTH__BACKEND__OIDC__*` | Server's own OIDC client_credentials for upstream APIs |
| **Backend LDAP** | `auth.backend.ldap` | `MCP__AUTH__BACKEND__LDAP__*` | Server's own LDAP service-account bind for upstream APIs |
| **Backend Static** | `auth.backend.static` | `MCP__AUTH__BACKEND__STATIC__*` | Server's own static token/cookie for upstream APIs |
**Frontend (inbound)** — validates bearer tokens presented by AI agents (MCP clients).
Only applies to `--transport http`; stdio has no network boundary. Standard OIDC JWT
bearer validation per RFC 9728 / MCP Authorization spec.
**Backend (outbound)** — credentials the MCP server uses to call upstream APIs.
Token priority (first available wins):
1. **OIDC** client_credentials grant (`auth.backend.oidc.*`)
2. **LDAP** service account bind (`auth.backend.ldap.*`)
3. **Static** bearer token (`auth.backend.static.bearer_token`)
The AI agent's inbound token is **never** forwarded upstream (MCP spec: Token Passthrough Prohibition).
Cookie-based auth is also available via `auth.backend.static.cookie_token`.
## Configuration
Create `~/.jira-mcp/config.yaml` (run `--print-default-config` for a full template):
```yaml
upstream:
endpoint: "" # upstream API base URL (e.g. https://api.example.com)
# When true, the MCP session ID is forwarded as X-MCP-Session-ID header.
enable_mcp_session_forward: true
runtime:
download_dir: ""
log_authorization: false
# ---- Authentication ----
# frontend = inbound: validates bearer tokens presented by MCP clients (AI agents).
# backend = outbound: credentials the server uses to call upstream APIs.
# The two are independent — the inbound token is never reused outbound
# (see MCP Authorization spec: Token Passthrough Prohibition).
auth:
frontend:
# Standard OAuth 2.1 / OIDC JWT bearer validation (Resource Server role, RFC 9728).
# Only enforced on the http transport; stdio has no network boundary.
oidc:
enabled: false
issuer: "" # e.g. https://idp.example.com/realms/myrealm
jwks_uri: "" # optional override (auto-discovered from issuer if empty)
audience: "" # expected JWT "aud" claim; also published as the RFC 9728 protected-resource identifier
# When true, validated frontend JWT claims (sub, email) are forwarded upstream
# as X-MCP-Client-Token-Sub and X-MCP-Client-Token-Email headers.
enable_client_token_claim_forward: true
# Additional JWT claims to forward as X-MCP-Client-Token-<Header-Name> headers.
# e.g. ["given_name", "family_name", "preferred_username"]
# additional_client_token_claim_forward: []
# Outbound: credentials the server uses to call upstream APIs.
backend:
oidc:
enabled: true
issuer: https://idp.example.com/realms/myrealm
client_id: my-client
client_secret: "${MCP__AUTH__BACKEND__OIDC__CLIENT_SECRET}"
scopes: openid
# token_url: "" # auto-discovered from issuer /.well-known
ldap:
enabled: false
url: ldaps://ldap.example.com
base_dn: dc=example,dc=com
bind_dn: cn=svc,dc=example,dc=com
bind_password: "${MCP__AUTH__BACKEND__LDAP__BIND_PASSWORD}"
static:
bearer_token: "${MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN}" # or set cookie_token
nativeTools:
expose:
register_all_tools_by_default: true # expose all native tools
# includes: [Tool1, Tool2] # or whitelist specific tools
# excludes: [Tool3] # hide noisy tools
```
All values can be set via `MCP__`-prefixed environment variables (e.g. `MCP__AUTH__BACKEND__OIDC__CLIENT_ID`).
## Management — Metrics, Tracing, Pprof
```yaml
mgmt:
enabled: true
host: 0.0.0.0
port: 9991
otel:
enabled: false
endpoint: localhost:4317
protocol: grpc
sample_rate: 1.0
metrics:
enabled: true
prometheus: true
export_interval: 30s
pprof:
enabled: false
server_bind: 0.0.0.0:6669
```
```sh
./bin/jira-mcp --transport http --port 8080
curl http://localhost:9991/metrics # Prometheus endpoint
curl http://localhost:9991/health # health check
```
Trace spans are named `mcp.tool.<name>`. W3C `traceparent` is propagated to upstream calls.
## Available Tools (435)
| Tool | Description |
|------|-------------|
| `AcknowledgeErrors` | Retry cluster upgrade - Retries the cluster upgrade. |
| `AddActorUsers` | Add actor to project role - Adds an actor (user or group) to a project role. For user actors, their usernames should be used. |
| `AddAttachment` | Add one or more attachments to an issue - Add one or more attachments to an issue. This resource expects a multipart post. The media-type multipart/form-data is defined in RFC 1867. Most client libraries have classes that make dealing with multipart posts simple. For instance, in Java the Apache HTTP Components library provides a MultiPartEntity that makes it simple to submit a multipart POST. In order to protect against XSRF attacks, because this method accepts multipart/form-data, it has XSRF protection on it. This means you must submit a header of X-Atlassian-Token: no-check with the request, otherwise it will be blocked. The name of the multipart/form-data parameter that contains attachments must be file. A simple example to upload a file called "myfile.txt" to issue TEST-123: curl -D- -u admin:admin -X POST -H "X-Atlassian-Token: no-check" -F "file=@myfile.txt" http://myhost/rest/api/2/issue/TEST-123/attachments |
| `AddComment` | Add a comment - Adds a new comment to an issue. |
| `AddField` | Add field to a tab - Adds field to the given tab. |
| `AddFieldToDefaultScreen` | Add field to default screen - Moves field on the given tab. |
| `AddProjectAssociationsToScheme` | Add project associations to scheme - Adds additional projects to those already associated with the specified issue type scheme |
| `AddProjectRoleActorsToRole` | Adds default actors to a role - Adds default actors to the given role. The request data should contain a list of usernames or a list of groups to add. |
| `AddSharePermission` | Add share permissions to filter - Adds a share permissions to the given filter. Adding a global permission removes all previous permissions from the filter |
| `AddTab` | Create tab for a screen - Creates tab for given screen. |
| `AddUserToApplication1` | Add user to application - Add user to given application. Admin permission will be required to perform this operation. |
| `AddUserToGroup` | Add a user to a specified group - Adds given user to a group |
| `AddVote` | Add vote to issue - Adds voter (currently logged user) to particular ticket. You need to be logged in to use this method. |
| `AddWatcher1` | Add a user as watcher - Adds a user to an issue's watcher list. |
| `AddWorklog` | Add a worklog entry - Adds a new worklog entry to an issue. |
| … | *420 more tools not shown* |
## Virtual Tools
Compose native tools via a declarative pipeline DSL:
```yaml
virtualTools:
- name: MyVirtualTool
description: Chain multiple native tools together
inputSchema:
type: object
properties:
appId: { type: string }
required: [appId]
pipeline:
- id: getApp
kind: call
spec: { tool: GetApplication, args: { applicationId: "$input.appId" } }
- id: appName
kind: jq
spec: { from: "$getApp", expr: .name }
- id: done
kind: return
spec: { from: "$appName" }
```
Pipeline kinds: `call`, `jq`, `foreach`, `emit`, `return`.
## Agent Integration
### OpenCode
- `~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"jira-mcp": {
"type": "local",
"command": ["bash", "-c", "/path/to/jira-mcp"],
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
},
"enabled": true
}
}
}
```
### Claude Code
- `~/.claude.json`:
```json
{
"mcpServers": {
"jira-mcp": {
"command": "/path/to/jira-mcp",
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Codex CLI
- `~/.codex/config.toml`:
```toml
[mcp_servers.jira-mcp]
url = "http://localhost:8080/mcp"
bearer_token_env_var = "MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN"
```
### Cursor
- `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"jira-mcp": {
"command": "/path/to/jira-mcp",
"args": ["--transport", "stdio"],
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### OpenCode (Remote)
- `~/.config/opencode/opencode.json`:
```json
{
"mcp": {
"jira-mcp": {
"type": "remote",
"url": "http://localhost:8080/mcp",
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Claude Code (Remote)
- `~/.claude.json` (User MCPs):
```json
{
"mcpServers": {
"jira-mcp": {
"url": "http://localhost:8080/mcp",
"env": {
"MCP__UPSTREAM__ENDPOINT": "https://api.example.com",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN": "your-token",
"MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN_FILE": "/path/to/fallback/.credentials"
}
}
}
}
```
### Codex CLI (Remote)
- `~/.codex/config.toml`:
```toml
[mcp_servers.jira-mcp]
url = "http://localhost:8080/mcp"
bearer_token_env_var = "MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN"
```
### Cursor (Remote)
- `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"jira-mcp": {
"url": "http://localhost:8080/mcp"
}
}
}
```
## Deployment — Docker
### Build
```sh
make build-image
```
Or manually:
```sh
docker build -t ghcr.io/<YOUR_ORG>/jira-mcp:latest -f deploy/docker/Dockerfile .
docker push ghcr.io/<YOUR_ORG>/jira-mcp:latest
```
### Run
Pull the pre-built image from GitHub Container Registry:
```sh
docker pull ghcr.io/<YOUR_ORG>/jira-mcp:latest
```
First generate a default config, then mount it when running:
```sh
docker run --rm ghcr.io/<YOUR_ORG>/jira-mcp:latest --print-default-config > ~/.jira-mcp/config.yaml
```
- **Static bearer token**
```sh
docker run -d --name jira-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__STATIC__BEARER_TOKEN="YOUR_BEARER_TOKEN" \
-v ~/.jira-mcp:/home/mcp/.jira-mcp \
ghcr.io/<YOUR_ORG>/jira-mcp:latest \
--transport http --port 8080
```
- **OIDC backend auth**
```sh
docker run -d --name jira-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__OIDC__ENABLED=true \
-e MCP__AUTH__BACKEND__OIDC__ISSUER="https://idp.example.com/realms/myrealm" \
-e MCP__AUTH__BACKEND__OIDC__CLIENT_ID="my-client" \
-e MCP__AUTH__BACKEND__OIDC__CLIENT_SECRET="YOUR_CLIENT_SECRET" \
-v ~/.jira-mcp:/home/mcp/.jira-mcp \
ghcr.io/<YOUR_ORG>/jira-mcp:latest \
--transport http --port 8080
```
- **LDAP backend auth**
```sh
docker run -d --name jira-mcp \
-p 8080:8080 -p 9991:9991 \
-e MCP__UPSTREAM__ENDPOINT="https://api.example.com" \
-e MCP__AUTH__BACKEND__LDAP__ENABLED=true \
-e MCP__AUTH__BACKEND__LDAP__URL="ldaps://ldap.example.com" \
-e MCP__AUTH__BACKEND__LDAP__BASE_DN="dc=example,dc=com" \
-e MCP__AUTH__BACKEND__LDAP__BIND_DN="cn=svc,dc=example,dc=com" \
-e MCP__AUTH__BACKEND__LDAP__BIND_PASSWORD="YOUR_BIND_PASSWORD" \
-v ~/.jira-mcp:/home/mcp/.jira-mcp \
ghcr.io/<YOUR_ORG>/jira-mcp:latest \
--transport http --port 8080
```
### Test with mcpclient.sh
After the container starts, use the bundled `mcpclient.sh` to verify.
> **Note:** `mcpclient.sh` only supports **backend static** token auth. If the server requires
> **frontend OIDC/LDAP** authentication, use a real AI-agent MCP client instead.
```sh
# Point to the mapped host port (include /mcp path)
export MCP_SERVER_ENDPOINT=http://localhost:18080/mcp
# Call any tool (example: AcknowledgeErrors)
./mcpclient.sh call AcknowledgeErrors '{"<param>":"<value>"}'
# List all available tools
./mcpclient.sh list-tools
```
> **Tip:** Map the host config directory for persistent configuration:
> `-v ~/.jira-mcp:/home/mcp/.jira-mcp`
## Deployment — Kubernetes (Helm)
First generate a default config, then mount it when running:
```sh
docker run --rm ghcr.io/<YOUR_ORG>/jira-mcp:latest --print-default-config > ~/.jira-mcp/config.yaml
```
- **Static bearer token**
```sh
helm upgrade -i jira-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/jira-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.bearerToken="YOUR_BEARER_TOKEN" \
--set config.upstream.endpoint="https://api.example.com"
```
- **OIDC authentication**
```sh
helm upgrade -i jira-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/jira-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.oidcClientSecret="YOUR_OIDC_CLIENT_SECRET" \
--set config.auth.backend.oidc.enabled=true \
--set config.auth.backend.oidc.issuer="https://idp.example.com" \
--set config.auth.backend.oidc.clientId="my-client" \
--set config.upstream.endpoint="https://api.example.com"
```
- **LDAP authentication**
```sh
helm upgrade -i jira-mcp deploy/helm \
--set image.repository=ghcr.io/<YOUR_ORG>/jira-mcp \
--set image.tag=latest \
--set secret.static.create=true \
--set secret.static.ldapBindPassword="YOUR_LDAP_BIND_PASSWORD" \
--set config.auth.backend.ldap.enabled=true \
--set config.auth.backend.ldap.url="ldaps://ldap.example.com" \
--set config.auth.backend.ldap.bindDN="cn=svc,dc=example,dc=com" \
--set config.upstream.endpoint="https://api.example.com"
```
See `deploy/helm/values.yaml` for all configurable parameters.
## Contributing
We welcome contributions! Here's how you can help:
1. **Fork** the repository and create a feature branch
2. **Make your changes** — add features, fix bugs, or improve documentation
3. **Add tests** — every new feature or bug fix should include unit tests
4. **Run the test suite** — `make test` must pass with no regressions
5. **Submit a Pull Request** — describe what you changed and why
### Development Setup
```sh
git clone https://github.com/<YOUR_ORG>/jira-mcp.git
cd jira-mcp
make build
make test
```
### Code Style
- Follow standard Go conventions (`go fmt`, `go vet`)
- Keep functions small and focused
- Write clear commit messages
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines and our [Code of Conduct](CODE_OF_CONDUCT.md).
## License
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
## Contact
For questions, suggestions, or support:
- **GitHub Issues**: [Create an issue](../../jira-mcp/issues)
- **Discussions**: [Start a discussion](../../jira-mcp/discussions)
- **Email**: <jameswong1376@gmail.com>
## Acknowledgments
This MCP server was generated by [MCPFather](https://github.com/flowgent-labs/mcpfather), an enterprise-grade OpenAPI-to-MCP code generator.
Built with these excellent open-source projects:
- [mcp-go](https://github.com/mark3labs/mcp-go) — Go SDK for the Model Context Protocol
- [OpenTelemetry](https://opentelemetry.io/) — observability framework for metrics and tracing
- [Prometheus](https://prometheus.io/) — metrics collection and alerting
- [Viper](https://github.com/spf13/viper) — Go configuration management
- [go-jq](https://github.com/itchyny/gojq) — Pure Go implementation of jq
- [Keycloak](https://www.keycloak.org/) — Open Source Identity and Access Management
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available