mcp-ticketer 0.1.17__tar.gz → 0.1.19__tar.gz
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.
Potentially problematic release.
This version of mcp-ticketer might be problematic. Click here for more details.
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/PKG-INFO +1 -1
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/pyproject.toml +1 -1
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/__version__.py +1 -1
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/linear.py +129 -52
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/main.py +9 -3
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/project_config.py +3 -3
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/queue.py +20 -5
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/worker.py +14 -3
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/PKG-INFO +1 -1
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/.dependency_cache +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/.mpm_deployment_state +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/agent-manager.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/agentic-coder-optimizer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/api_qa.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/clerk-ops.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/code_analyzer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/content-agent.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/dart_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/data_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/documentation.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/gcp_ops_agent.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/golang_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/imagemagick.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/java_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/local_ops_agent.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/memory_manager.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/nextjs_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/ops.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/php-engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/product_owner.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/project_organizer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/prompt-engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/python_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/qa.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/react_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/refactoring_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/research.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/ruby-engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/rust_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/security.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/ticketing.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/typescript_engineer.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/vercel_ops_agent.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/version_control.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/web_qa.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude/agents/web_ui.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/config/project.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/mcp_auto_config_preference.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/README.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/agentic_coder_optimizer_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/documentation_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/engineer_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/ops_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/project_knowledge.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/qa_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/research_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/version-control_memories.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude-mpm/memories/workflows.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.claude.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.coveragerc +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.env.example +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.github/workflows/docs.yml +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.github/workflows/publish.yml +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.github/workflows/test.yml +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.mcp/config.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.pre-commit-config.yaml +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.python-version +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/.readthedocs.yaml +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CHANGELOG.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CLAUDE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CLAUDE_DESKTOP_SETUP.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CODE_STRUCTURE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CONFIG_RESOLUTION_FIX.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CONTRIBUTING.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/CREDENTIAL_VALIDATION_FIX.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/ENV_DISCOVERY_COMPLETE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/HIERARCHY_IMPLEMENTATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/IMPLEMENTATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/JIRA_SETUP.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/LICENSE +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/LINEAR_SETUP.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/MANIFEST.in +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/Makefile +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/OPTIMIZATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/PROJECT_INITIALIZATION_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/QUEUE_SYSTEM.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/QUICK_START.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/README.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/RELEASE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/RELEASING.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/TEST_COVERAGE_REPORT.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/TEST_REPORT.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/TEST_RESULTS_SUMMARY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/claude-desktop-config.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/debug_search.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/debug_test.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/ADAPTERS.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/API_REFERENCE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/CONFIGURATION.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/CONFIG_RESOLUTION_FLOW.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/DEVELOPER_GUIDE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/ENV_DISCOVERY.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/MCP_INTEGRATION.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/MIGRATION_GUIDE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/PROJECT_CONFIG.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/PR_INTEGRATION.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/QUICK_START_ENV.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/USER_GUIDE.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_archive/README.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/ADAPTERS.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/API_REFERENCE.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/CONFIGURATION.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/DEVELOPER_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/MCP_INTEGRATION.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/MIGRATION_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/USER_GUIDE.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/adapters/github.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/adapters.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/api.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/cli.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/development.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/examples.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/index.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/installation.rst.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/_build/_sources/prd/mcp-ticketer-prd.md.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/adapters/github.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/adapters.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/api.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/cli.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/conf.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/development.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/examples.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/index.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/installation.rst +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/prd/mcp-ticketer-prd.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/docs/requirements.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/install.sh +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/mcp-ticketer +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/mcp-ticketer-server +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/mcp_server.sh +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/pytest.ini +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/requirements-dev.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/requirements.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/scripts/README.md +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/scripts/manage_version.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/setup.cfg +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/setup.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/aitrackdown.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/github.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/hybrid.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/adapters/jira.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cache/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cache/memory.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/configure.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/discover.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/mcp_configure.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/migrate_config.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/queue_commands.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/cli/utils.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/adapter.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/config.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/env_discovery.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/http_client.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/mappers.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/models.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/core/registry.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/mcp/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/mcp/server.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/py.typed +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/__main__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/manager.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer/queue/run_worker.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/SOURCES.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/dependency_links.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/entry_points.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/not-zip-safe +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/requires.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/src/mcp_ticketer.egg-info/top_level.txt +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test-tickets/tickets/task-20250924002724.json +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_all_adapters.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_api_usage.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_basic.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_comprehensive.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_config_resolution.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_credential_validation.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_error_handling.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_github.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_github_token.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_jira.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_linear.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_linear_native.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_linear_teams.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_mcp_server_qa.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_optimizations.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_performance.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_pr_functionality.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_queue_system.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_serve_config.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/test_set_command.sh +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/adapters/__init__.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/adapters/test_aitrackdown.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/conftest.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/core/test_env_discovery.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/test_base_adapter.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/test_models.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tests/test_queue.py +0 -0
- {mcp_ticketer-0.1.17 → mcp_ticketer-0.1.19}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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>
|
|
@@ -277,8 +277,11 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
277
277
|
config: Configuration with:
|
|
278
278
|
- api_key: Linear API key (or LINEAR_API_KEY env var)
|
|
279
279
|
- workspace: Linear workspace name (optional, for documentation)
|
|
280
|
-
- team_key: Linear team key (
|
|
280
|
+
- team_key: Linear team key (e.g., 'BTA') OR
|
|
281
|
+
- team_id: Linear team UUID (e.g., '02d15669-7351-4451-9719-807576c16049')
|
|
281
282
|
- api_url: Optional Linear API URL
|
|
283
|
+
|
|
284
|
+
Note: Either team_key or team_id is required. If both are provided, team_id takes precedence.
|
|
282
285
|
"""
|
|
283
286
|
super().__init__(config)
|
|
284
287
|
|
|
@@ -288,18 +291,16 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
288
291
|
raise ValueError("Linear API key required (config.api_key or LINEAR_API_KEY env var)")
|
|
289
292
|
|
|
290
293
|
self.workspace = config.get("workspace") # Optional, for documentation
|
|
291
|
-
self.team_key = config.get("team_key")
|
|
292
|
-
if not self.team_key:
|
|
293
|
-
raise ValueError("Linear team_key is required in configuration")
|
|
294
|
-
self.api_url = config.get("api_url", "https://api.linear.app/graphql")
|
|
295
294
|
|
|
296
|
-
#
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
295
|
+
# Support both team_key (short key) and team_id (UUID)
|
|
296
|
+
self.team_key = config.get("team_key") # Short key like "BTA"
|
|
297
|
+
self.team_id_config = config.get("team_id") # UUID like "02d15669-..."
|
|
298
|
+
|
|
299
|
+
# Require at least one team identifier
|
|
300
|
+
if not self.team_key and not self.team_id_config:
|
|
301
|
+
raise ValueError("Either team_key or team_id is required in configuration")
|
|
302
|
+
|
|
303
|
+
self.api_url = config.get("api_url", "https://api.linear.app/graphql")
|
|
303
304
|
|
|
304
305
|
# Caches for frequently used data
|
|
305
306
|
self._team_id: Optional[str] = None
|
|
@@ -314,6 +315,22 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
314
315
|
self._init_lock = asyncio.Lock()
|
|
315
316
|
self._initialized = False
|
|
316
317
|
|
|
318
|
+
def _create_client(self) -> Client:
|
|
319
|
+
"""Create a fresh GraphQL client for each operation.
|
|
320
|
+
|
|
321
|
+
This prevents 'Transport is already connected' errors by ensuring
|
|
322
|
+
each operation gets its own client and transport instance.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Client: Fresh GraphQL client instance
|
|
326
|
+
"""
|
|
327
|
+
transport = HTTPXAsyncTransport(
|
|
328
|
+
url=self.api_url,
|
|
329
|
+
headers={"Authorization": self.api_key},
|
|
330
|
+
timeout=30.0,
|
|
331
|
+
)
|
|
332
|
+
return Client(transport=transport, fetch_schema_from_transport=False)
|
|
333
|
+
|
|
317
334
|
async def initialize(self) -> None:
|
|
318
335
|
"""Initialize adapter by preloading team, states, and labels data concurrently."""
|
|
319
336
|
if self._initialized:
|
|
@@ -347,9 +364,36 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
347
364
|
raise e
|
|
348
365
|
|
|
349
366
|
async def _fetch_team_data(self) -> str:
|
|
350
|
-
"""Fetch team ID.
|
|
367
|
+
"""Fetch team ID.
|
|
368
|
+
|
|
369
|
+
If team_id is configured, validate it exists and return it.
|
|
370
|
+
If team_key is configured, fetch the team_id by key.
|
|
371
|
+
"""
|
|
372
|
+
# If team_id (UUID) is provided, use it directly (preferred)
|
|
373
|
+
if self.team_id_config:
|
|
374
|
+
# Validate that this team ID exists
|
|
375
|
+
query = gql("""
|
|
376
|
+
query GetTeamById($id: String!) {
|
|
377
|
+
team(id: $id) {
|
|
378
|
+
id
|
|
379
|
+
name
|
|
380
|
+
key
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
""")
|
|
384
|
+
|
|
385
|
+
client = self._create_client()
|
|
386
|
+
async with client as session:
|
|
387
|
+
result = await session.execute(query, variable_values={"id": self.team_id_config})
|
|
388
|
+
|
|
389
|
+
if not result.get("team"):
|
|
390
|
+
raise ValueError(f"Team with ID '{self.team_id_config}' not found")
|
|
391
|
+
|
|
392
|
+
return result["team"]["id"]
|
|
393
|
+
|
|
394
|
+
# Otherwise, fetch team ID by key
|
|
351
395
|
query = gql("""
|
|
352
|
-
query
|
|
396
|
+
query GetTeamByKey($key: String!) {
|
|
353
397
|
teams(filter: { key: { eq: $key } }) {
|
|
354
398
|
nodes {
|
|
355
399
|
id
|
|
@@ -360,7 +404,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
360
404
|
}
|
|
361
405
|
""")
|
|
362
406
|
|
|
363
|
-
|
|
407
|
+
client = self._create_client()
|
|
408
|
+
async with client as session:
|
|
364
409
|
result = await session.execute(query, variable_values={"key": self.team_key})
|
|
365
410
|
|
|
366
411
|
if not result["teams"]["nodes"]:
|
|
@@ -384,7 +429,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
384
429
|
}
|
|
385
430
|
""")
|
|
386
431
|
|
|
387
|
-
|
|
432
|
+
client = self._create_client()
|
|
433
|
+
async with client as session:
|
|
388
434
|
result = await session.execute(query, variable_values={"teamId": team_id})
|
|
389
435
|
|
|
390
436
|
workflow_states = {}
|
|
@@ -410,7 +456,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
410
456
|
}
|
|
411
457
|
""")
|
|
412
458
|
|
|
413
|
-
|
|
459
|
+
client = self._create_client()
|
|
460
|
+
async with client as session:
|
|
414
461
|
result = await session.execute(query, variable_values={"teamId": team_id})
|
|
415
462
|
|
|
416
463
|
return {label["name"]: label["id"] for label in result["issueLabels"]["nodes"]}
|
|
@@ -451,7 +498,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
451
498
|
}
|
|
452
499
|
""")
|
|
453
500
|
|
|
454
|
-
|
|
501
|
+
client = self._create_client()
|
|
502
|
+
async with client as session:
|
|
455
503
|
result = await session.execute(
|
|
456
504
|
search_query,
|
|
457
505
|
variable_values={"name": name, "teamId": team_id}
|
|
@@ -481,7 +529,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
481
529
|
if color:
|
|
482
530
|
label_input["color"] = color
|
|
483
531
|
|
|
484
|
-
|
|
532
|
+
client = self._create_client()
|
|
533
|
+
async with client as session:
|
|
485
534
|
result = await session.execute(
|
|
486
535
|
create_query,
|
|
487
536
|
variable_values={"input": label_input}
|
|
@@ -510,7 +559,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
510
559
|
}
|
|
511
560
|
""")
|
|
512
561
|
|
|
513
|
-
|
|
562
|
+
client = self._create_client()
|
|
563
|
+
async with client as session:
|
|
514
564
|
result = await session.execute(query, variable_values={"email": email})
|
|
515
565
|
|
|
516
566
|
if result["users"]["nodes"]:
|
|
@@ -528,8 +578,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
528
578
|
"""
|
|
529
579
|
if not self.api_key:
|
|
530
580
|
return False, "LINEAR_API_KEY is required but not found. Set it in .env.local or environment."
|
|
531
|
-
if not self.team_key:
|
|
532
|
-
return False, "Linear team_key is required in configuration. Set it in .mcp-ticketer/config.json"
|
|
581
|
+
if not self.team_key and not self.team_id_config:
|
|
582
|
+
return False, "Either Linear team_key or team_id is required in configuration. Set it in .mcp-ticketer/config.json"
|
|
533
583
|
return True, ""
|
|
534
584
|
|
|
535
585
|
def _get_state_mapping(self) -> Dict[TicketState, str]:
|
|
@@ -792,7 +842,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
792
842
|
}
|
|
793
843
|
}
|
|
794
844
|
""")
|
|
795
|
-
|
|
845
|
+
client = self._create_client()
|
|
846
|
+
async with client as session:
|
|
796
847
|
parent_result = await session.execute(
|
|
797
848
|
parent_query,
|
|
798
849
|
variable_values={"identifier": ticket.parent_issue}
|
|
@@ -824,7 +875,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
824
875
|
}
|
|
825
876
|
""")
|
|
826
877
|
|
|
827
|
-
|
|
878
|
+
client = self._create_client()
|
|
879
|
+
async with client as session:
|
|
828
880
|
result = await session.execute(
|
|
829
881
|
create_query,
|
|
830
882
|
variable_values={"input": issue_input}
|
|
@@ -852,7 +904,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
852
904
|
""")
|
|
853
905
|
|
|
854
906
|
try:
|
|
855
|
-
|
|
907
|
+
client = self._create_client()
|
|
908
|
+
async with client as session:
|
|
856
909
|
result = await session.execute(
|
|
857
910
|
query,
|
|
858
911
|
variable_values={"identifier": ticket_id}
|
|
@@ -882,7 +935,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
882
935
|
}
|
|
883
936
|
""")
|
|
884
937
|
|
|
885
|
-
|
|
938
|
+
client = self._create_client()
|
|
939
|
+
async with client as session:
|
|
886
940
|
result = await session.execute(
|
|
887
941
|
query,
|
|
888
942
|
variable_values={"identifier": ticket_id}
|
|
@@ -958,7 +1012,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
958
1012
|
}
|
|
959
1013
|
""")
|
|
960
1014
|
|
|
961
|
-
|
|
1015
|
+
client = self._create_client()
|
|
1016
|
+
async with client as session:
|
|
962
1017
|
result = await session.execute(
|
|
963
1018
|
update_query,
|
|
964
1019
|
variable_values={"id": linear_id, "input": update_input}
|
|
@@ -985,7 +1040,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
985
1040
|
}
|
|
986
1041
|
""")
|
|
987
1042
|
|
|
988
|
-
|
|
1043
|
+
client = self._create_client()
|
|
1044
|
+
async with client as session:
|
|
989
1045
|
result = await session.execute(
|
|
990
1046
|
query,
|
|
991
1047
|
variable_values={"identifier": ticket_id}
|
|
@@ -1005,7 +1061,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1005
1061
|
}
|
|
1006
1062
|
""")
|
|
1007
1063
|
|
|
1008
|
-
|
|
1064
|
+
client = self._create_client()
|
|
1065
|
+
async with client as session:
|
|
1009
1066
|
result = await session.execute(
|
|
1010
1067
|
archive_query,
|
|
1011
1068
|
variable_values={"id": linear_id}
|
|
@@ -1101,7 +1158,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1101
1158
|
}
|
|
1102
1159
|
""")
|
|
1103
1160
|
|
|
1104
|
-
|
|
1161
|
+
client = self._create_client()
|
|
1162
|
+
async with client as session:
|
|
1105
1163
|
result = await session.execute(
|
|
1106
1164
|
query,
|
|
1107
1165
|
variable_values={
|
|
@@ -1175,7 +1233,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1175
1233
|
}
|
|
1176
1234
|
""")
|
|
1177
1235
|
|
|
1178
|
-
|
|
1236
|
+
client = self._create_client()
|
|
1237
|
+
async with client as session:
|
|
1179
1238
|
result = await session.execute(
|
|
1180
1239
|
search_query,
|
|
1181
1240
|
variable_values={
|
|
@@ -1215,7 +1274,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1215
1274
|
}
|
|
1216
1275
|
""")
|
|
1217
1276
|
|
|
1218
|
-
|
|
1277
|
+
client = self._create_client()
|
|
1278
|
+
async with client as session:
|
|
1219
1279
|
result = await session.execute(
|
|
1220
1280
|
query,
|
|
1221
1281
|
variable_values={"identifier": comment.ticket_id}
|
|
@@ -1247,7 +1307,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1247
1307
|
if comment.metadata and "parent_comment_id" in comment.metadata:
|
|
1248
1308
|
comment_input["parentId"] = comment.metadata["parent_comment_id"]
|
|
1249
1309
|
|
|
1250
|
-
|
|
1310
|
+
client = self._create_client()
|
|
1311
|
+
async with client as session:
|
|
1251
1312
|
result = await session.execute(
|
|
1252
1313
|
create_comment_query,
|
|
1253
1314
|
variable_values={"input": comment_input}
|
|
@@ -1292,7 +1353,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1292
1353
|
""")
|
|
1293
1354
|
|
|
1294
1355
|
try:
|
|
1295
|
-
|
|
1356
|
+
client = self._create_client()
|
|
1357
|
+
async with client as session:
|
|
1296
1358
|
result = await session.execute(
|
|
1297
1359
|
query,
|
|
1298
1360
|
variable_values={
|
|
@@ -1348,7 +1410,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1348
1410
|
if description:
|
|
1349
1411
|
project_input["description"] = description
|
|
1350
1412
|
|
|
1351
|
-
|
|
1413
|
+
client = self._create_client()
|
|
1414
|
+
async with client as session:
|
|
1352
1415
|
result = await session.execute(
|
|
1353
1416
|
create_query,
|
|
1354
1417
|
variable_values={"input": project_input}
|
|
@@ -1389,7 +1452,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1389
1452
|
}
|
|
1390
1453
|
""")
|
|
1391
1454
|
|
|
1392
|
-
|
|
1455
|
+
client = self._create_client()
|
|
1456
|
+
async with client as session:
|
|
1393
1457
|
result = await session.execute(
|
|
1394
1458
|
query,
|
|
1395
1459
|
variable_values={"filter": cycle_filter}
|
|
@@ -1424,7 +1488,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1424
1488
|
}
|
|
1425
1489
|
""")
|
|
1426
1490
|
|
|
1427
|
-
|
|
1491
|
+
client = self._create_client()
|
|
1492
|
+
async with client as session:
|
|
1428
1493
|
result = await session.execute(
|
|
1429
1494
|
create_query,
|
|
1430
1495
|
variable_values={
|
|
@@ -1502,7 +1567,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1502
1567
|
},
|
|
1503
1568
|
}
|
|
1504
1569
|
|
|
1505
|
-
|
|
1570
|
+
client = self._create_client()
|
|
1571
|
+
async with client as session:
|
|
1506
1572
|
result = await session.execute(
|
|
1507
1573
|
create_query,
|
|
1508
1574
|
variable_values={"input": attachment_input}
|
|
@@ -1601,7 +1667,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1601
1667
|
raise ValueError(f"Could not find Linear ID for issue {ticket_id}")
|
|
1602
1668
|
linear_id = search_result["id"]
|
|
1603
1669
|
|
|
1604
|
-
|
|
1670
|
+
client = self._create_client()
|
|
1671
|
+
async with client as session:
|
|
1605
1672
|
result = await session.execute(
|
|
1606
1673
|
update_query,
|
|
1607
1674
|
variable_values={
|
|
@@ -1648,7 +1715,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1648
1715
|
""")
|
|
1649
1716
|
|
|
1650
1717
|
try:
|
|
1651
|
-
|
|
1718
|
+
client = self._create_client()
|
|
1719
|
+
async with client as session:
|
|
1652
1720
|
result = await session.execute(
|
|
1653
1721
|
search_query,
|
|
1654
1722
|
variable_values={"identifier": identifier}
|
|
@@ -1701,7 +1769,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1701
1769
|
if "lead_id" in kwargs:
|
|
1702
1770
|
project_input["leadId"] = kwargs["lead_id"]
|
|
1703
1771
|
|
|
1704
|
-
|
|
1772
|
+
client = self._create_client()
|
|
1773
|
+
async with client as session:
|
|
1705
1774
|
result = await session.execute(
|
|
1706
1775
|
create_query,
|
|
1707
1776
|
variable_values={"input": project_input}
|
|
@@ -1731,7 +1800,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1731
1800
|
""")
|
|
1732
1801
|
|
|
1733
1802
|
try:
|
|
1734
|
-
|
|
1803
|
+
client = self._create_client()
|
|
1804
|
+
async with client as session:
|
|
1735
1805
|
result = await session.execute(
|
|
1736
1806
|
query,
|
|
1737
1807
|
variable_values={"id": epic_id}
|
|
@@ -1780,7 +1850,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1780
1850
|
}
|
|
1781
1851
|
""")
|
|
1782
1852
|
|
|
1783
|
-
|
|
1853
|
+
client = self._create_client()
|
|
1854
|
+
async with client as session:
|
|
1784
1855
|
result = await session.execute(
|
|
1785
1856
|
query,
|
|
1786
1857
|
variable_values={
|
|
@@ -1847,7 +1918,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1847
1918
|
""")
|
|
1848
1919
|
|
|
1849
1920
|
try:
|
|
1850
|
-
|
|
1921
|
+
client = self._create_client()
|
|
1922
|
+
async with client as session:
|
|
1851
1923
|
result = await session.execute(
|
|
1852
1924
|
query,
|
|
1853
1925
|
variable_values={"projectId": epic_id, "first": 100}
|
|
@@ -1900,7 +1972,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1900
1972
|
}
|
|
1901
1973
|
""")
|
|
1902
1974
|
|
|
1903
|
-
|
|
1975
|
+
client = self._create_client()
|
|
1976
|
+
async with client as session:
|
|
1904
1977
|
parent_result = await session.execute(
|
|
1905
1978
|
parent_query,
|
|
1906
1979
|
variable_values={"identifier": parent_id}
|
|
@@ -1965,7 +2038,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1965
2038
|
}
|
|
1966
2039
|
""")
|
|
1967
2040
|
|
|
1968
|
-
|
|
2041
|
+
client = self._create_client()
|
|
2042
|
+
async with client as session:
|
|
1969
2043
|
result = await session.execute(
|
|
1970
2044
|
create_query,
|
|
1971
2045
|
variable_values={"input": issue_input}
|
|
@@ -1999,7 +2073,8 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
1999
2073
|
""")
|
|
2000
2074
|
|
|
2001
2075
|
try:
|
|
2002
|
-
|
|
2076
|
+
client = self._create_client()
|
|
2077
|
+
async with client as session:
|
|
2003
2078
|
result = await session.execute(
|
|
2004
2079
|
query,
|
|
2005
2080
|
variable_values={"identifier": issue_id}
|
|
@@ -2020,11 +2095,13 @@ class LinearAdapter(BaseAdapter[Task]):
|
|
|
2020
2095
|
return []
|
|
2021
2096
|
|
|
2022
2097
|
async def close(self) -> None:
|
|
2023
|
-
"""Close the GraphQL client connection.
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2098
|
+
"""Close the GraphQL client connection.
|
|
2099
|
+
|
|
2100
|
+
Since we create fresh clients for each operation, there's no persistent
|
|
2101
|
+
connection to close. Each client's transport is automatically closed when
|
|
2102
|
+
the async context manager exits.
|
|
2103
|
+
"""
|
|
2104
|
+
pass
|
|
2028
2105
|
|
|
2029
2106
|
|
|
2030
2107
|
# Register the adapter
|
|
@@ -70,18 +70,24 @@ class AdapterType(str, Enum):
|
|
|
70
70
|
GITHUB = "github"
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def load_config() -> dict:
|
|
73
|
+
def load_config(project_dir: Optional[Path] = None) -> dict:
|
|
74
74
|
"""Load configuration from file.
|
|
75
75
|
|
|
76
|
+
Args:
|
|
77
|
+
project_dir: Optional project directory to load config from
|
|
78
|
+
|
|
76
79
|
Resolution order:
|
|
77
|
-
1. Project-specific config (.mcp-ticketer/config.json in cwd)
|
|
80
|
+
1. Project-specific config (.mcp-ticketer/config.json in project_dir or cwd)
|
|
78
81
|
2. Global config (~/.mcp-ticketer/config.json)
|
|
79
82
|
|
|
80
83
|
Returns:
|
|
81
84
|
Configuration dictionary
|
|
82
85
|
"""
|
|
86
|
+
# Use provided project_dir or current working directory
|
|
87
|
+
base_dir = project_dir or Path.cwd()
|
|
88
|
+
|
|
83
89
|
# Check project-specific config first
|
|
84
|
-
project_config =
|
|
90
|
+
project_config = base_dir / ".mcp-ticketer" / "config.json"
|
|
85
91
|
if project_config.exists():
|
|
86
92
|
try:
|
|
87
93
|
with open(project_config, "r") as f:
|
|
@@ -211,9 +211,9 @@ class ConfigValidator:
|
|
|
211
211
|
if field not in config or not config[field]:
|
|
212
212
|
return False, f"Linear config missing required field: {field}"
|
|
213
213
|
|
|
214
|
-
#
|
|
215
|
-
if not config.get("team_id"):
|
|
216
|
-
|
|
214
|
+
# Require either team_key or team_id (team_id is preferred)
|
|
215
|
+
if not config.get("team_key") and not config.get("team_id"):
|
|
216
|
+
return False, "Linear config requires either team_key (short key like 'BTA') or team_id (UUID)"
|
|
217
217
|
|
|
218
218
|
return True, None
|
|
219
219
|
|
|
@@ -32,6 +32,7 @@ class QueueItem:
|
|
|
32
32
|
error_message: Optional[str] = None
|
|
33
33
|
retry_count: int = 0
|
|
34
34
|
result: Optional[Dict[str, Any]] = None
|
|
35
|
+
project_dir: Optional[str] = None
|
|
35
36
|
|
|
36
37
|
def to_dict(self) -> dict:
|
|
37
38
|
"""Convert to dictionary for storage."""
|
|
@@ -54,7 +55,8 @@ class QueueItem:
|
|
|
54
55
|
processed_at=datetime.fromisoformat(row[6]) if row[6] else None,
|
|
55
56
|
error_message=row[7],
|
|
56
57
|
retry_count=row[8],
|
|
57
|
-
result=json.loads(row[9]) if row[9] else None
|
|
58
|
+
result=json.loads(row[9]) if row[9] else None,
|
|
59
|
+
project_dir=row[10] if len(row) > 10 else None
|
|
58
60
|
)
|
|
59
61
|
|
|
60
62
|
|
|
@@ -109,31 +111,43 @@ class Queue:
|
|
|
109
111
|
ON queue(adapter)
|
|
110
112
|
''')
|
|
111
113
|
|
|
114
|
+
# Migration: Add project_dir column if it doesn't exist
|
|
115
|
+
cursor = conn.execute("PRAGMA table_info(queue)")
|
|
116
|
+
columns = [row[1] for row in cursor.fetchall()]
|
|
117
|
+
if 'project_dir' not in columns:
|
|
118
|
+
conn.execute('ALTER TABLE queue ADD COLUMN project_dir TEXT')
|
|
119
|
+
|
|
112
120
|
conn.commit()
|
|
113
121
|
|
|
114
122
|
def add(self,
|
|
115
123
|
ticket_data: Dict[str, Any],
|
|
116
124
|
adapter: str,
|
|
117
|
-
operation: str
|
|
125
|
+
operation: str,
|
|
126
|
+
project_dir: Optional[str] = None) -> str:
|
|
118
127
|
"""Add item to queue.
|
|
119
128
|
|
|
120
129
|
Args:
|
|
121
130
|
ticket_data: The ticket data for the operation
|
|
122
131
|
adapter: Name of the adapter to use
|
|
123
132
|
operation: Operation to perform (create, update, delete, etc.)
|
|
133
|
+
project_dir: Project directory for config resolution (defaults to current directory)
|
|
124
134
|
|
|
125
135
|
Returns:
|
|
126
136
|
Queue ID for tracking
|
|
127
137
|
"""
|
|
128
138
|
queue_id = f"Q-{uuid.uuid4().hex[:8].upper()}"
|
|
129
139
|
|
|
140
|
+
# Default to current working directory if not provided
|
|
141
|
+
if project_dir is None:
|
|
142
|
+
project_dir = str(Path.cwd())
|
|
143
|
+
|
|
130
144
|
with self._lock:
|
|
131
145
|
with sqlite3.connect(self.db_path) as conn:
|
|
132
146
|
conn.execute('''
|
|
133
147
|
INSERT INTO queue (
|
|
134
148
|
id, ticket_data, adapter, operation,
|
|
135
|
-
status, created_at, retry_count
|
|
136
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
149
|
+
status, created_at, retry_count, project_dir
|
|
150
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
137
151
|
''', (
|
|
138
152
|
queue_id,
|
|
139
153
|
json.dumps(ticket_data),
|
|
@@ -141,7 +155,8 @@ class Queue:
|
|
|
141
155
|
operation,
|
|
142
156
|
QueueStatus.PENDING.value,
|
|
143
157
|
datetime.now().isoformat(),
|
|
144
|
-
0
|
|
158
|
+
0,
|
|
159
|
+
project_dir
|
|
145
160
|
))
|
|
146
161
|
conn.commit()
|
|
147
162
|
|
|
@@ -303,15 +303,26 @@ class Worker:
|
|
|
303
303
|
Returns:
|
|
304
304
|
Adapter instance
|
|
305
305
|
"""
|
|
306
|
-
# Load configuration
|
|
306
|
+
# Load configuration from the project directory where the item was created
|
|
307
307
|
from ..cli.main import load_config
|
|
308
|
+
from pathlib import Path
|
|
309
|
+
import os
|
|
310
|
+
|
|
311
|
+
# Use item's project_dir if available, otherwise use current directory
|
|
312
|
+
project_path = Path(item.project_dir) if item.project_dir else None
|
|
308
313
|
|
|
309
|
-
|
|
314
|
+
# Load environment variables from project directory's .env.local if it exists
|
|
315
|
+
if project_path:
|
|
316
|
+
env_file = project_path / ".env.local"
|
|
317
|
+
if env_file.exists():
|
|
318
|
+
logger.debug(f"Loading environment from {env_file}")
|
|
319
|
+
load_dotenv(env_file)
|
|
320
|
+
|
|
321
|
+
config = load_config(project_dir=project_path)
|
|
310
322
|
adapters_config = config.get("adapters", {})
|
|
311
323
|
adapter_config = adapters_config.get(item.adapter, {})
|
|
312
324
|
|
|
313
325
|
# Add environment variables for authentication
|
|
314
|
-
import os
|
|
315
326
|
if item.adapter == "linear":
|
|
316
327
|
if not adapter_config.get("api_key"):
|
|
317
328
|
adapter_config["api_key"] = os.getenv("LINEAR_API_KEY")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-ticketer
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|