mcp-ticketer 0.1.39__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1652,56 +1652,191 @@ async def main():
1652
1652
  # Load configuration
1653
1653
  import json
1654
1654
  import logging
1655
+ import os
1655
1656
  from pathlib import Path
1656
1657
 
1657
1658
  logger = logging.getLogger(__name__)
1658
1659
 
1659
- # ONLY read from project-local config, never from user home
1660
- config_file = Path.cwd() / ".mcp-ticketer" / "config.json"
1661
- if config_file.exists():
1662
- # Validate config is within project
1663
- try:
1664
- if not config_file.resolve().is_relative_to(Path.cwd().resolve()):
1665
- logger.error(
1666
- f"Security violation: Config file {config_file} "
1667
- "is not within project directory"
1668
- )
1669
- raise ValueError(
1670
- f"Security violation: Config file {config_file} "
1671
- "is not within project directory"
1672
- )
1673
- except (ValueError, RuntimeError):
1674
- # is_relative_to may raise ValueError in some cases
1675
- pass
1660
+ # Initialize defaults
1661
+ adapter_type = "aitrackdown"
1662
+ adapter_config = {"base_path": ".aitrackdown"}
1663
+
1664
+ # Priority 1: Check .env files (highest priority for MCP)
1665
+ env_config = _load_env_configuration()
1666
+ if env_config and env_config.get("adapter_type"):
1667
+ adapter_type = env_config["adapter_type"]
1668
+ adapter_config = env_config["adapter_config"]
1669
+ logger.info(f"Using adapter from .env files: {adapter_type}")
1670
+ logger.info(f"Built adapter config from .env: {list(adapter_config.keys())}")
1671
+ else:
1672
+ # Priority 2: Check project-local config file
1673
+ config_file = Path.cwd() / ".mcp-ticketer" / "config.json"
1674
+ if config_file.exists():
1675
+ # Validate config is within project
1676
+ try:
1677
+ if not config_file.resolve().is_relative_to(Path.cwd().resolve()):
1678
+ logger.error(
1679
+ f"Security violation: Config file {config_file} "
1680
+ "is not within project directory"
1681
+ )
1682
+ raise ValueError(
1683
+ f"Security violation: Config file {config_file} "
1684
+ "is not within project directory"
1685
+ )
1686
+ except (ValueError, RuntimeError):
1687
+ # is_relative_to may raise ValueError in some cases
1688
+ pass
1676
1689
 
1677
- try:
1678
- with open(config_file) as f:
1679
- config = json.load(f)
1680
- adapter_type = config.get("default_adapter", "aitrackdown")
1681
- # Get adapter-specific config
1682
- adapters_config = config.get("adapters", {})
1683
- adapter_config = adapters_config.get(adapter_type, {})
1684
- # Fallback to legacy config format
1685
- if not adapter_config and "config" in config:
1686
- adapter_config = config["config"]
1687
- logger.info(
1688
- f"Loaded MCP configuration from project-local: {config_file}"
1689
- )
1690
- except (OSError, json.JSONDecodeError) as e:
1691
- logger.warning(f"Could not load project config: {e}, using defaults")
1690
+ try:
1691
+ with open(config_file) as f:
1692
+ config = json.load(f)
1693
+ adapter_type = config.get("default_adapter", "aitrackdown")
1694
+ # Get adapter-specific config
1695
+ adapters_config = config.get("adapters", {})
1696
+ adapter_config = adapters_config.get(adapter_type, {})
1697
+ # Fallback to legacy config format
1698
+ if not adapter_config and "config" in config:
1699
+ adapter_config = config["config"]
1700
+ logger.info(
1701
+ f"Loaded MCP configuration from project-local: {config_file}"
1702
+ )
1703
+ except (OSError, json.JSONDecodeError) as e:
1704
+ logger.warning(f"Could not load project config: {e}, using defaults")
1705
+ adapter_type = "aitrackdown"
1706
+ adapter_config = {"base_path": ".aitrackdown"}
1707
+ else:
1708
+ # Priority 3: Default to aitrackdown
1709
+ logger.info("No configuration found, defaulting to aitrackdown adapter")
1692
1710
  adapter_type = "aitrackdown"
1693
1711
  adapter_config = {"base_path": ".aitrackdown"}
1694
- else:
1695
- # Default to aitrackdown with local base path
1696
- logger.info("No project-local config found, defaulting to aitrackdown adapter")
1697
- adapter_type = "aitrackdown"
1698
- adapter_config = {"base_path": ".aitrackdown"}
1712
+
1713
+ # Log final configuration for debugging
1714
+ logger.info(f"Starting MCP server with adapter: {adapter_type}")
1715
+ logger.debug(f"Adapter config keys: {list(adapter_config.keys())}")
1699
1716
 
1700
1717
  # Create and run server
1701
1718
  server = MCPTicketServer(adapter_type, adapter_config)
1702
1719
  await server.run()
1703
1720
 
1704
1721
 
1722
+ def _load_env_configuration() -> Optional[dict[str, Any]]:
1723
+ """Load adapter configuration from .env files.
1724
+
1725
+ Checks .env.local first (highest priority), then .env.
1726
+
1727
+ Returns:
1728
+ Dictionary with 'adapter_type' and 'adapter_config' keys, or None if no config found
1729
+ """
1730
+ from pathlib import Path
1731
+
1732
+ # Check for .env files in order of preference
1733
+ env_files = [".env.local", ".env"]
1734
+ env_vars = {}
1735
+
1736
+ for env_file in env_files:
1737
+ env_path = Path.cwd() / env_file
1738
+ if env_path.exists():
1739
+ try:
1740
+ # Parse .env file manually to avoid external dependencies
1741
+ with open(env_path, 'r') as f:
1742
+ for line in f:
1743
+ line = line.strip()
1744
+ if line and not line.startswith('#') and '=' in line:
1745
+ key, value = line.split('=', 1)
1746
+ key = key.strip()
1747
+ value = value.strip().strip('"').strip("'")
1748
+ if value: # Only add non-empty values
1749
+ env_vars[key] = value
1750
+ except Exception:
1751
+ continue
1752
+
1753
+ if not env_vars:
1754
+ return None
1755
+
1756
+ # Determine adapter type and build config
1757
+ adapter_type = env_vars.get("MCP_TICKETER_ADAPTER")
1758
+ if not adapter_type:
1759
+ # Auto-detect based on available keys
1760
+ if any(key.startswith("LINEAR_") for key in env_vars):
1761
+ adapter_type = "linear"
1762
+ elif any(key.startswith("GITHUB_") for key in env_vars):
1763
+ adapter_type = "github"
1764
+ elif any(key.startswith("JIRA_") for key in env_vars):
1765
+ adapter_type = "jira"
1766
+ else:
1767
+ return None
1768
+
1769
+ # Build adapter-specific configuration
1770
+ adapter_config = _build_adapter_config_from_env_vars(adapter_type, env_vars)
1771
+
1772
+ if not adapter_config:
1773
+ return None
1774
+
1775
+ return {
1776
+ "adapter_type": adapter_type,
1777
+ "adapter_config": adapter_config
1778
+ }
1779
+
1780
+
1781
+ def _build_adapter_config_from_env_vars(adapter_type: str, env_vars: dict[str, str]) -> dict[str, Any]:
1782
+ """Build adapter configuration from parsed environment variables.
1783
+
1784
+ Args:
1785
+ adapter_type: Type of adapter to configure
1786
+ env_vars: Dictionary of environment variables from .env files
1787
+
1788
+ Returns:
1789
+ Dictionary of adapter configuration
1790
+ """
1791
+ config = {}
1792
+
1793
+ if adapter_type == "linear":
1794
+ # Linear adapter configuration
1795
+ if env_vars.get("LINEAR_API_KEY"):
1796
+ config["api_key"] = env_vars["LINEAR_API_KEY"]
1797
+ if env_vars.get("LINEAR_TEAM_ID"):
1798
+ config["team_id"] = env_vars["LINEAR_TEAM_ID"]
1799
+ if env_vars.get("LINEAR_TEAM_KEY"):
1800
+ config["team_key"] = env_vars["LINEAR_TEAM_KEY"]
1801
+ if env_vars.get("LINEAR_API_URL"):
1802
+ config["api_url"] = env_vars["LINEAR_API_URL"]
1803
+
1804
+ elif adapter_type == "github":
1805
+ # GitHub adapter configuration
1806
+ if env_vars.get("GITHUB_TOKEN"):
1807
+ config["token"] = env_vars["GITHUB_TOKEN"]
1808
+ if env_vars.get("GITHUB_OWNER"):
1809
+ config["owner"] = env_vars["GITHUB_OWNER"]
1810
+ if env_vars.get("GITHUB_REPO"):
1811
+ config["repo"] = env_vars["GITHUB_REPO"]
1812
+
1813
+ elif adapter_type == "jira":
1814
+ # JIRA adapter configuration
1815
+ if env_vars.get("JIRA_SERVER"):
1816
+ config["server"] = env_vars["JIRA_SERVER"]
1817
+ if env_vars.get("JIRA_EMAIL"):
1818
+ config["email"] = env_vars["JIRA_EMAIL"]
1819
+ if env_vars.get("JIRA_API_TOKEN"):
1820
+ config["api_token"] = env_vars["JIRA_API_TOKEN"]
1821
+ if env_vars.get("JIRA_PROJECT_KEY"):
1822
+ config["project_key"] = env_vars["JIRA_PROJECT_KEY"]
1823
+
1824
+ elif adapter_type == "aitrackdown":
1825
+ # AITrackdown adapter configuration
1826
+ base_path = env_vars.get("MCP_TICKETER_BASE_PATH", ".aitrackdown")
1827
+ config["base_path"] = base_path
1828
+ config["auto_create_dirs"] = True
1829
+
1830
+ # Add any generic overrides
1831
+ if env_vars.get("MCP_TICKETER_API_KEY"):
1832
+ config["api_key"] = env_vars["MCP_TICKETER_API_KEY"]
1833
+
1834
+ return config
1835
+
1836
+
1837
+
1838
+
1839
+
1705
1840
  # Add diagnostic handler methods to MCPTicketServer class
1706
1841
  async def _handle_system_health(self, arguments: dict[str, Any]) -> dict[str, Any]:
1707
1842
  """Handle system health check."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-ticketer
3
- Version: 0.1.39
3
+ Version: 0.3.0
4
4
  Summary: Universal ticket management interface for AI agents with MCP support
5
5
  Author-email: MCP Ticketer Team <support@mcp-ticketer.io>
6
6
  Maintainer-email: MCP Ticketer Team <support@mcp-ticketer.io>
@@ -1,15 +1,22 @@
1
1
  mcp_ticketer/__init__.py,sha256=Xx4WaprO5PXhVPbYi1L6tBmwmJMkYS-lMyG4ieN6QP0,717
2
- mcp_ticketer/__version__.py,sha256=RcgpUO8aLxkc0dVDN1OI3liNLe1LdvT5ar10c-XmM8E,1118
2
+ mcp_ticketer/__version__.py,sha256=GoFXSWeevjribCSU_VeE2nxxgWFRp71QCl67iMs_z50,1117
3
3
  mcp_ticketer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  mcp_ticketer/adapters/__init__.py,sha256=B5DFllWn23hkhmrLykNO5uMMSdcFuuPHXyLw_jyFzuE,358
5
5
  mcp_ticketer/adapters/aitrackdown.py,sha256=stlbge8K6w-EyQkw_vEQNSXQgCOWN5tOlQUgGWZQNMQ,17936
6
6
  mcp_ticketer/adapters/github.py,sha256=rhf8yaK9vOGMstvFwddguWnokVIfpasoYMBlyjNg_vY,47335
7
7
  mcp_ticketer/adapters/hybrid.py,sha256=UADYZLc_UNw0xHPSbgguBNzvUCnuYn12Qi9ea-zdlMk,19086
8
8
  mcp_ticketer/adapters/jira.py,sha256=IoyMaznYE7NliaNGv-I_q2UodUS6JhOllLP1gUEXThs,35375
9
- mcp_ticketer/adapters/linear.py,sha256=Ze_DqmFoKvHFIjHYiAMX7LViumQu0wyKgThWWOfV3Do,75906
9
+ mcp_ticketer/adapters/linear.py,sha256=fcQ9s_V7IgP4z2K12lbWXYHUVs1avu1uzUzDCK2eEg4,552
10
+ mcp_ticketer/adapters/linear/__init__.py,sha256=dOPx6caymloIsdyjxS33E09gfPQS6kprisDWwOT2-bE,735
11
+ mcp_ticketer/adapters/linear/adapter.py,sha256=emvjHU-NSFVdxS7lxRjcTv6QNxn26-8RbZNg-x-z5xE,26641
12
+ mcp_ticketer/adapters/linear/client.py,sha256=6IKgYRNy0GF6MhXwxaEBeBE2FM-d1WfhOQ43Mhj0-3s,8871
13
+ mcp_ticketer/adapters/linear/mappers.py,sha256=96euA9TuOTEWEPDKvixjYHb82Ha1zD6gxuo9VsLT2wA,9636
14
+ mcp_ticketer/adapters/linear/queries.py,sha256=chcHCHqvyQbKHPp9wWHGo3HdUGV_6RHNdRadP2pZkVQ,7112
15
+ mcp_ticketer/adapters/linear/types.py,sha256=VuGPu1Z5jGHtbI2zkCyL5YFnwQNukGW-UV72k6zF0p4,8097
10
16
  mcp_ticketer/cache/__init__.py,sha256=Xcd-cKnt-Cx7jBzvfzUUUPaGkmyXFi5XUFWw3Z4b7d4,138
11
17
  mcp_ticketer/cache/memory.py,sha256=2yBqGi9i0SanlUhJoOC7nijWjoMa3_ntPe-V-AV-LfU,5042
12
18
  mcp_ticketer/cli/__init__.py,sha256=l9Q8iKmfGkTu0cssHBVqNZTsL4eAtFzOB25AED_0G6g,89
19
+ mcp_ticketer/cli/adapter_diagnostics.py,sha256=t7RyesBRSyJBoVvft2scdNRcugJDIWvqYS9NszPV5ok,14820
13
20
  mcp_ticketer/cli/auggie_configure.py,sha256=MXKzLtqe3K_UTQ2GacHAWbvf_B0779KL325smiAKE0Q,8212
14
21
  mcp_ticketer/cli/codex_configure.py,sha256=xDppHouT6_-cYXswyAggoPX5bSlRXMvCoM_x9PQ-42A,9086
15
22
  mcp_ticketer/cli/configure.py,sha256=BsA_pSHQMQS0t1bJO_wMM8LWsd5sWJDASjEPRHvwC18,16198
@@ -17,7 +24,7 @@ mcp_ticketer/cli/diagnostics.py,sha256=AC7cMQHVWdHfrYH2Y1tkhmRezM2-mID5E_Dhfil7F
17
24
  mcp_ticketer/cli/discover.py,sha256=AF_qlQc1Oo0UkWayoF5pmRChS5J3fJjH6f2YZzd_k8w,13188
18
25
  mcp_ticketer/cli/gemini_configure.py,sha256=ZNSA1lBW-itVToza-JxW95Po7daVXKiZAh7lp6pmXMU,9343
19
26
  mcp_ticketer/cli/linear_commands.py,sha256=b5v4c9uBNPwj_vdy314JkLZbyC1fXU6IcY2VoG0z7gI,17193
20
- mcp_ticketer/cli/main.py,sha256=9e83BCxds9AESIYueK1VjeJ96H7AvRbvPiMYwd4EeKs,62258
27
+ mcp_ticketer/cli/main.py,sha256=EK2MgphV1Anw_vAtkTl5iC-vzPWIOyu3SV7zOTAY_Vw,72131
21
28
  mcp_ticketer/cli/mcp_configure.py,sha256=RzV50UjXgOmvMp-9S0zS39psuvjffVByaMrqrUaAGAM,9594
22
29
  mcp_ticketer/cli/migrate_config.py,sha256=MYsr_C5ZxsGg0P13etWTWNrJ_lc6ElRCkzfQADYr3DM,5956
23
30
  mcp_ticketer/cli/queue_commands.py,sha256=mm-3H6jmkUGJDyU_E46o9iRpek8tvFCm77F19OtHiZI,7884
@@ -28,13 +35,14 @@ mcp_ticketer/core/adapter.py,sha256=q64LxOInIno7EIbmuxItf8KEsd-g9grCs__Z4uwZHto,
28
35
  mcp_ticketer/core/config.py,sha256=hvn8RoN2BSmYzESKqCvcpKleX0PrGiHVQ5v35nX12Mc,19241
29
36
  mcp_ticketer/core/env_discovery.py,sha256=It62C97UBt96CgVbZCql5NQtONmCHMkX3c8W2kEl-Qk,18716
30
37
  mcp_ticketer/core/env_loader.py,sha256=fX8EpHcAJxxhPJ-XY5BD8HlLs-Y9jdlQ24Qtt5ylBNo,12032
38
+ mcp_ticketer/core/exceptions.py,sha256=78tzV3Muc7E_UhEIByF0NtsPe1I0lFVrbpdDNX56kQg,3737
31
39
  mcp_ticketer/core/http_client.py,sha256=s5ikMiwEJ8TJjNn73wu3gv3OdAtyBEpAqPnSroRMW2k,13971
32
40
  mcp_ticketer/core/mappers.py,sha256=1aG1jFsHTCwmGRVgOlXW-VOSTGzc86gv7qjDfiR1ups,17462
33
41
  mcp_ticketer/core/models.py,sha256=a_2AbL3NlN0pfdZad-hXus_zb4bSi9zISHcsNYl0sng,12486
34
42
  mcp_ticketer/core/project_config.py,sha256=yYxlgxjcEPeOwx-b-SXFpe0k9pW9xzBRAK72PsItG-o,23346
35
43
  mcp_ticketer/core/registry.py,sha256=ShYLDPE62KFJpB0kj_zFyQzRxSH3LkQEEuo1jaakb1k,3483
36
44
  mcp_ticketer/mcp/__init__.py,sha256=Y05eTzsPk0wH8yKNIM-ekpGjgSDO0bQr0EME-vOP4GE,123
37
- mcp_ticketer/mcp/server.py,sha256=vs0WR9_KhDaYyhrkbTuTsEThT0bMj6gwuFmAxmzCxwU,76377
45
+ mcp_ticketer/mcp/server.py,sha256=PCNerYqLMq5et3ZYAIdd_q_-4LGLBhDuvPAYTuWxDiQ,81351
38
46
  mcp_ticketer/queue/__init__.py,sha256=1YIaCpZpFqPcqvDEQXiEvDLiw94DXRdCJkBaVIFQrms,231
39
47
  mcp_ticketer/queue/__main__.py,sha256=gc_tE9NUdK07OJfTZuD4t6KeBD_vxFQIhknGTQUG_jk,109
40
48
  mcp_ticketer/queue/health_monitor.py,sha256=aQrlBzfbLWu8-fV2b5CuHs4oqyTqGGcntKIHM3r-dDI,11844
@@ -43,9 +51,9 @@ mcp_ticketer/queue/queue.py,sha256=jSAkYNEIbNH1cbYuF8s6eFuZmXqn8WHXx3mbfMU2Ud8,1
43
51
  mcp_ticketer/queue/run_worker.py,sha256=F7anuhdkgZF9lXZntHuJ7rEzuEkAfAZO1qvGh3R57bw,1033
44
52
  mcp_ticketer/queue/ticket_registry.py,sha256=k8FYg2cFYsI4POb94-o-fTrIVr-ttfi60r0O5YhJYck,15321
45
53
  mcp_ticketer/queue/worker.py,sha256=zXJpyhRJ99be0VLaez3YPtC9OU17vVNu5qhr1dCGaLg,19992
46
- mcp_ticketer-0.1.39.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
47
- mcp_ticketer-0.1.39.dist-info/METADATA,sha256=K3lSazYkpXThzS7r5FdBZpyqN6Ey_ruWst8_FG6MxtU,13220
48
- mcp_ticketer-0.1.39.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- mcp_ticketer-0.1.39.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
50
- mcp_ticketer-0.1.39.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
51
- mcp_ticketer-0.1.39.dist-info/RECORD,,
54
+ mcp_ticketer-0.3.0.dist-info/licenses/LICENSE,sha256=KOVrunjtILSzY-2N8Lqa3-Q8dMaZIG4LrlLTr9UqL08,1073
55
+ mcp_ticketer-0.3.0.dist-info/METADATA,sha256=qUgh1KFBMCfrmkev4QBVmCEpbCAbutnjGDv5pTrfyJ8,13219
56
+ mcp_ticketer-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ mcp_ticketer-0.3.0.dist-info/entry_points.txt,sha256=o1IxVhnHnBNG7FZzbFq-Whcs1Djbofs0qMjiUYBLx2s,60
58
+ mcp_ticketer-0.3.0.dist-info/top_level.txt,sha256=WnAG4SOT1Vm9tIwl70AbGG_nA217YyV3aWFhxLH2rxw,13
59
+ mcp_ticketer-0.3.0.dist-info/RECORD,,