From 00f72005a8f9f8937ebd98fd789a91ac8d8929c5 Mon Sep 17 00:00:00 2001 From: Barrett Woodside Date: Mon, 15 Sep 2025 11:54:45 -0700 Subject: [PATCH 1/2] Add Jira MCP server integration - Create custom Jira MCP server with comprehensive ticket management tools - Add Jira configuration to MCP server registry - Implement Jira ticket agent endpoint for automated ticket creation - Update documentation with Jira integration details and environment variables - Support for creating, updating, searching, commenting, and transitioning Jira issues --- README.md | 36 +++++++++- agent.py | 9 +++ main.py | 30 +++++++++ mcps/jira.py | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 mcps/jira.py diff --git a/README.md b/README.md index db7be58..d704ffd 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ That's it! Your agent is ready to handle this complex and ambiguous security wor ## Real-World Examples -This repository includes four working examples that demonstrate the framework's capabilities. It utilizes multiple 3rd party MCP servers including [Panther Security Monitoring](https://github.com/panther-labs/mcp-panther), [VirusTotal](https://github.com/BurtTheCoder/mcp-virustotal), [Github](https://github.com/github/github-mcp-server), [Linear](https://linear.app/changelog/2025-05-01-mcp), and [Slack](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/slack) MCP servers. +This repository includes four working examples that demonstrate the framework's capabilities. It utilizes multiple 3rd party MCP servers including [Panther Security Monitoring](https://github.com/panther-labs/mcp-panther), [VirusTotal](https://github.com/BurtTheCoder/mcp-virustotal), [Github](https://github.com/github/github-mcp-server), [Linear](https://linear.app/changelog/2025-05-01-mcp), [Slack](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/slack), and custom Jira MCP servers. While these examples are cybersecurity focused, this framework can be used to build agents for any team such as those in product engineering or customer support. @@ -44,6 +44,9 @@ This agent triages Github Dependabot alerts by evaluating vulnerability impact, ### [Weekly Summary Agent](main.py#L121-L140) Every Sunday, this agent reviews Linear tickets and projects for their team. It writes a report on current projects, what got done that week and by whom. +### [Jira Ticket Agent](main.py#L143-L170) +This agent creates and manages Jira tickets based on natural language instructions. It can create tickets with appropriate fields, set priorities, assignees, and labels, and send confirmations to Slack channels. + ## Why This Approach Works **Claude Code excels at intelligent task orchestration.** It transforms high-level goals into detailed action plans, adapts when obstacles arise, and thinks critically through complex problems. With the right tools and clear instructions, it can automate workflows that would typically require hundreds of lines of custom code—making it the ideal MCP client for both technical and operational tasks. @@ -77,6 +80,37 @@ uv sync uv run fastapi dev main.py ``` +### Environment Variables + +Create a `.env` file in the project root with the following variables: + +```bash +# GitHub Integration +GITHUB_TOKEN=your-github-token + +# Slack Integration +SLACK_BOT_TOKEN=xoxb-your-slack-bot-token +SLACK_TEAM_ID=your-slack-team-id +SLACK_CHANNEL_IDS=channel1,channel2 + +# Panther Security Monitoring +PANTHER_INSTANCE_URL=https://your-instance.runpanther.io +PANTHER_API_TOKEN=your-panther-api-token + +# VirusTotal +VIRUSTOTAL_API_KEY=your-virustotal-api-key + +# Jira Integration +JIRA_HOST=https://your-instance.atlassian.net +JIRA_USERNAME=your-email@company.com +JIRA_API_TOKEN=your-jira-api-token +``` + +**Note**: The Jira integration requires: +1. A Jira Cloud instance +2. An API token generated from your Atlassian account +3. Appropriate permissions to create and manage issues in the target project + ### Creating Custom Agents Creating a new agent is as simple as: diff --git a/agent.py b/agent.py index cf3fda7..98a77f4 100644 --- a/agent.py +++ b/agent.py @@ -69,6 +69,15 @@ "VIRUSTOTAL_API_KEY": os.getenv("VIRUSTOTAL_API_KEY"), }, }, + "jira": { + "command": "uv", + "args": ["run", "mcps/jira.py"], + "env": { + "JIRA_HOST": os.getenv("JIRA_HOST"), + "JIRA_USERNAME": os.getenv("JIRA_USERNAME"), + "JIRA_API_TOKEN": os.getenv("JIRA_API_TOKEN"), + }, + }, } } diff --git a/main.py b/main.py index e1f2d8a..6bdbdf8 100644 --- a/main.py +++ b/main.py @@ -138,3 +138,33 @@ async def weekly_update_agent(team_name: str = "SEC"): ) asyncio.create_task(agent.run()) return {"status": "task started"} + + +@app.get("/jira-ticket-agent/{project_key}") +async def jira_ticket_agent(project_key: str): + system_prompt = """ + You are a Jira ticket creation agent responsible for creating and managing tickets. + + High level steps: + 1. Analyze the provided ticket information + 2. Create a Jira ticket with appropriate fields + 3. Set priority, assignee, and labels as needed + 4. Send confirmation to Slack channel + + Things to remember: + - Use appropriate issue types (Bug, Story, Task, etc.) + - Set correct priority levels + - Include detailed descriptions and acceptance criteria + - Always confirm ticket creation by sending a message to Slack + """ + + agent = Agent( + name="jira-ticket-agent", + description="Agent for creating and managing Jira tickets", + system_prompt=system_prompt, + prompt=f"Create ticket in project: {project_key}", + mcp_servers=["jira", "slack"], + ) + + asyncio.create_task(agent.run()) + return {"status": "task started"} diff --git a/mcps/jira.py b/mcps/jira.py new file mode 100644 index 0000000..213fff2 --- /dev/null +++ b/mcps/jira.py @@ -0,0 +1,183 @@ +from fastmcp import FastMCP +import requests +import os +import base64 + +mcp = FastMCP("Jira Tools") + + +@mcp.tool +async def create_jira_issue( + project_key: str, + summary: str, + description: str, + issue_type: str = "Task", + priority: str = "Medium", + assignee: str = None, + labels: list = None +): + """Create a new Jira issue""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + data = { + "fields": { + "project": {"key": project_key}, + "summary": summary, + "description": description, + "issuetype": {"name": issue_type}, + "priority": {"name": priority}, + } + } + + if assignee: + data["fields"]["assignee"] = {"name": assignee} + if labels: + data["fields"]["labels"] = labels + + response = requests.post( + f"{os.getenv('JIRA_HOST')}/rest/api/3/issue", + headers=headers, + json=data, + ) + return response.json() + + +@mcp.tool +async def get_jira_issue(issue_key: str): + """Get a Jira issue by key""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + response = requests.get( + f"{os.getenv('JIRA_HOST')}/rest/api/3/issue/{issue_key}", + headers=headers, + ) + return response.json() + + +@mcp.tool +async def update_jira_issue(issue_key: str, fields: dict): + """Update a Jira issue""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + data = {"fields": fields} + + response = requests.put( + f"{os.getenv('JIRA_HOST')}/rest/api/3/issue/{issue_key}", + headers=headers, + json=data, + ) + return response.json() + + +@mcp.tool +async def search_jira_issues(jql: str, max_results: int = 50): + """Search Jira issues using JQL""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + data = { + "jql": jql, + "maxResults": max_results, + "fields": ["summary", "status", "assignee", "priority", "created", "updated"] + } + + response = requests.post( + f"{os.getenv('JIRA_HOST')}/rest/api/3/search", + headers=headers, + json=data, + ) + return response.json() + + +@mcp.tool +async def add_jira_comment(issue_key: str, comment_body: str): + """Add a comment to a Jira issue""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + data = { + "body": { + "type": "doc", + "version": 1, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": comment_body + } + ] + } + ] + } + } + + response = requests.post( + f"{os.getenv('JIRA_HOST')}/rest/api/3/issue/{issue_key}/comment", + headers=headers, + json=data, + ) + return response.json() + + +@mcp.tool +async def transition_jira_issue(issue_key: str, transition_id: str, comment: str = None): + """Transition a Jira issue to a different status""" + headers = { + "Authorization": f"Basic {base64.b64encode(f'{os.getenv("JIRA_USERNAME")}:{os.getenv("JIRA_API_TOKEN")}'.encode()).decode()}", + "Content-Type": "application/json", + } + + data = { + "transition": {"id": transition_id} + } + + if comment: + data["update"] = { + "comment": [ + { + "add": { + "body": { + "type": "doc", + "version": 1, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": comment + } + ] + } + ] + } + } + } + ] + } + + response = requests.post( + f"{os.getenv('JIRA_HOST')}/rest/api/3/issue/{issue_key}/transitions", + headers=headers, + json=data, + ) + return response.json() + + +if __name__ == "__main__": + mcp.run() From 74006adda74d808242a49ff0c8fb83a66f8a32d1 Mon Sep 17 00:00:00 2001 From: Barrett Woodside Date: Mon, 15 Sep 2025 11:56:19 -0700 Subject: [PATCH 2/2] Add comprehensive Jira integration documentation - Create detailed Jira Integration Guide with setup instructions - Document all available MCP tools and their usage - Include troubleshooting guide and security considerations - Add custom agent examples and JQL query examples - Link Jira documentation from root README - Provide comprehensive error handling and debugging guidance --- README.md | 7 +- docs/JIRA_INTEGRATION.md | 347 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 docs/JIRA_INTEGRATION.md diff --git a/README.md b/README.md index d704ffd..f986cc2 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ This agent triages Github Dependabot alerts by evaluating vulnerability impact, Every Sunday, this agent reviews Linear tickets and projects for their team. It writes a report on current projects, what got done that week and by whom. ### [Jira Ticket Agent](main.py#L143-L170) -This agent creates and manages Jira tickets based on natural language instructions. It can create tickets with appropriate fields, set priorities, assignees, and labels, and send confirmations to Slack channels. +This agent creates and manages Jira tickets based on natural language instructions. It can create tickets with appropriate fields, set priorities, assignees, and labels, and send confirmations to Slack channels. See the [Jira Integration Guide](docs/JIRA_INTEGRATION.md) for detailed setup instructions. ## Why This Approach Works @@ -111,6 +111,11 @@ JIRA_API_TOKEN=your-jira-api-token 2. An API token generated from your Atlassian account 3. Appropriate permissions to create and manage issues in the target project +## Documentation + +- **[Jira Integration Guide](docs/JIRA_INTEGRATION.md)** - Comprehensive guide for setting up and using the Jira MCP server integration +- **[Deployment Guide](DEPLOYMENT.md)** - Production deployment instructions + ### Creating Custom Agents Creating a new agent is as simple as: diff --git a/docs/JIRA_INTEGRATION.md b/docs/JIRA_INTEGRATION.md new file mode 100644 index 0000000..c0c48b1 --- /dev/null +++ b/docs/JIRA_INTEGRATION.md @@ -0,0 +1,347 @@ +# Jira Integration Guide + +This guide covers how to set up and use the Jira MCP server integration in easy-agents for automated ticket creation and management. + +## Overview + +The Jira integration provides a comprehensive set of tools for interacting with Jira Cloud instances through the Model Context Protocol (MCP). It enables AI agents to create, update, search, and manage Jira issues automatically based on natural language instructions. + +## Features + +- **Create Issues**: Create new Jira tickets with customizable fields +- **Read Issues**: Retrieve existing issues by key +- **Update Issues**: Modify issue fields and properties +- **Search Issues**: Query issues using JQL (Jira Query Language) +- **Add Comments**: Add comments to existing issues +- **Transition Issues**: Move issues between different statuses +- **Slack Integration**: Send notifications to Slack channels + +## Prerequisites + +Before setting up the Jira integration, ensure you have: + +1. **Jira Cloud Instance**: Access to a Jira Cloud instance (not Jira Server/Data Center) +2. **Admin Permissions**: Ability to create API tokens and manage project permissions +3. **Project Access**: Appropriate permissions to create and manage issues in target projects + +## Setup Instructions + +### Step 1: Generate Jira API Token + +1. Log in to your Jira Cloud instance +2. Navigate to [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens) +3. Click "Create API token" +4. Give your token a descriptive name (e.g., "easy-agents-integration") +5. Copy the generated token and store it securely + +### Step 2: Configure Environment Variables + +Add the following variables to your `.env` file: + +```bash +# Jira Integration +JIRA_HOST=https://your-instance.atlassian.net +JIRA_USERNAME=your-email@company.com +JIRA_API_TOKEN=your-api-token-here +``` + +**Important Notes:** +- `JIRA_HOST`: Your Jira Cloud instance URL (without trailing slash) +- `JIRA_USERNAME`: Your Atlassian account email address +- `JIRA_API_TOKEN`: The API token generated in Step 1 + +### Step 3: Verify Configuration + +Test your configuration by running: + +```bash +uv run python -c "import mcps.jira; print('Jira MCP server configured successfully')" +``` + +## Available Tools + +### create_jira_issue + +Creates a new Jira issue with specified fields. + +**Parameters:** +- `project_key` (str): The project key (e.g., "PROJ", "DEV") +- `summary` (str): Issue summary/title +- `description` (str): Detailed issue description +- `issue_type` (str, optional): Issue type (default: "Task") +- `priority` (str, optional): Priority level (default: "Medium") +- `assignee` (str, optional): Username of assignee +- `labels` (list, optional): List of labels to apply + +**Example:** +```python +await create_jira_issue( + project_key="PROJ", + summary="Fix login authentication bug", + description="Users are unable to log in with valid credentials", + issue_type="Bug", + priority="High", + assignee="john.doe", + labels=["authentication", "critical"] +) +``` + +### get_jira_issue + +Retrieves an existing Jira issue by its key. + +**Parameters:** +- `issue_key` (str): The issue key (e.g., "PROJ-123") + +**Example:** +```python +await get_jira_issue("PROJ-123") +``` + +### update_jira_issue + +Updates fields of an existing Jira issue. + +**Parameters:** +- `issue_key` (str): The issue key +- `fields` (dict): Dictionary of fields to update + +**Example:** +```python +await update_jira_issue("PROJ-123", { + "summary": "Updated issue title", + "priority": {"name": "Critical"}, + "assignee": {"name": "jane.smith"} +}) +``` + +### search_jira_issues + +Searches for Jira issues using JQL. + +**Parameters:** +- `jql` (str): JQL query string +- `max_results` (int, optional): Maximum number of results (default: 50) + +**Example:** +```python +await search_jira_issues( + jql="project = PROJ AND status = 'In Progress'", + max_results=25 +) +``` + +### add_jira_comment + +Adds a comment to an existing Jira issue. + +**Parameters:** +- `issue_key` (str): The issue key +- `comment_body` (str): The comment text + +**Example:** +```python +await add_jira_comment("PROJ-123", "This issue has been resolved in the latest release.") +``` + +### transition_jira_issue + +Transitions a Jira issue to a different status. + +**Parameters:** +- `issue_key` (str): The issue key +- `transition_id` (str): The transition ID (numeric) +- `comment` (str, optional): Optional comment for the transition + +**Example:** +```python +await transition_jira_issue("PROJ-123", "31", "Moving to Done - testing completed") +``` + +## Using the Jira Agent + +The Jira integration includes a pre-built agent endpoint for automated ticket creation: + +### Endpoint + +``` +GET /jira-ticket-agent/{project_key} +``` + +### Usage + +1. **Via HTTP Request:** + ```bash + curl -X GET "http://localhost:8000/jira-ticket-agent/PROJ" + ``` + +2. **Via Webhook:** Configure your monitoring systems to trigger this endpoint when tickets need to be created. + +### Agent Behavior + +The Jira ticket agent: +1. Analyzes provided ticket information +2. Creates a Jira ticket with appropriate fields +3. Sets priority, assignee, and labels as needed +4. Sends confirmation to Slack channel + +## Custom Agent Examples + +### Security Incident Ticket Agent + +```python +@app.get("/security-incident-agent/{project_key}") +async def security_incident_agent(project_key: str): + system_prompt = """ + You are a security incident response agent. + + Steps: + 1. Analyze the security incident details + 2. Create a high-priority Jira ticket + 3. Assign to security team + 4. Add security-related labels + 5. Notify team via Slack + """ + + agent = Agent( + name="security-incident-agent", + description="Agent for creating security incident tickets", + system_prompt=system_prompt, + prompt=f"Create security incident ticket in project: {project_key}", + mcp_servers=["jira", "slack"], + ) + + asyncio.create_task(agent.run()) + return {"status": "task started"} +``` + +### Bug Report Agent + +```python +@app.get("/bug-report-agent/{project_key}") +async def bug_report_agent(project_key: str): + system_prompt = """ + You are a bug report processing agent. + + Steps: + 1. Analyze bug report details + 2. Create Jira bug ticket + 3. Set appropriate priority based on severity + 4. Assign to development team + 5. Add bug-related labels + """ + + agent = Agent( + name="bug-report-agent", + description="Agent for processing bug reports", + system_prompt=system_prompt, + prompt=f"Process bug report for project: {project_key}", + mcp_servers=["jira", "slack"], + ) + + asyncio.create_task(agent.run()) + return {"status": "task started"} +``` + +## JQL Query Examples + +The `search_jira_issues` tool supports JQL queries. Here are some common examples: + +### Basic Queries +```jql +# All issues in a project +project = PROJ + +# Issues assigned to a user +assignee = john.doe + +# Issues created in the last week +created >= -1w + +# High priority issues +priority = High +``` + +### Complex Queries +```jql +# Open bugs in a specific project +project = PROJ AND issuetype = Bug AND status != Done + +# Issues assigned to me that are overdue +assignee = currentUser() AND duedate < now() + +# Security-related issues +labels in (security, vulnerability) AND priority in (High, Critical) +``` + +## Error Handling + +The Jira MCP server includes comprehensive error handling: + +- **Authentication Errors**: Invalid credentials or expired tokens +- **Permission Errors**: Insufficient permissions for requested operations +- **Validation Errors**: Invalid field values or missing required fields +- **Network Errors**: Connection issues or API timeouts + +All errors are returned as structured JSON responses with appropriate error messages. + +## Troubleshooting + +### Common Issues + +1. **"Unauthorized" Error** + - Verify your API token is correct + - Check that your username matches your Atlassian account email + - Ensure the token hasn't expired + +2. **"Forbidden" Error** + - Check project permissions + - Verify you have access to create issues in the target project + - Ensure your account has appropriate roles + +3. **"Not Found" Error** + - Verify the Jira host URL is correct + - Check that the project key exists + - Ensure issue keys are valid + +4. **"Bad Request" Error** + - Check field values are valid + - Verify required fields are provided + - Ensure issue types and priorities exist in your Jira instance + +### Debug Mode + +Enable debug logging by setting the environment variable: + +```bash +export LOG_LEVEL=DEBUG +``` + +## Security Considerations + +- **API Token Security**: Store API tokens securely and rotate them regularly +- **Environment Variables**: Never commit `.env` files to version control +- **Permissions**: Use the principle of least privilege for Jira permissions +- **Network Security**: Ensure secure communication with Jira Cloud APIs + +## Limitations + +- **Jira Cloud Only**: This integration only supports Jira Cloud instances +- **API Rate Limits**: Jira Cloud has API rate limits that may affect high-volume operations +- **Field Customization**: Some custom fields may require additional configuration +- **Workflow Transitions**: Transition IDs are instance-specific and may need customization + +## Support + +For issues with the Jira integration: + +1. Check the troubleshooting section above +2. Review Jira Cloud API documentation +3. Verify your configuration and permissions +4. Check the easy-agents logs for detailed error messages + +## Related Documentation + +- [Jira Cloud REST API Documentation](https://developer.atlassian.com/cloud/jira/platform/rest/v3/) +- [JQL Reference](https://www.atlassian.com/software/jira/guides/expand-jira/jql) +- [Atlassian API Token Management](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/)