# Bug Fix: URL Handling Without Router (v1.1.6)

## Issue

When users provided full URLs to ticket operations (like `ticket_read()`, `ticket_update()`, `ticket_delete()`, `ticket_assign()`) but the multi-platform router was not configured, the system would incorrectly pass the full URL to the adapter's methods instead of extracting the ticket ID first.

**Example Bug**:
```python
# User provides URL
ticket_read("https://linear.app/1m-hyperdev/view/mcp-skills-issues-0d0359fabcf9")

# BUG: Code would call adapter.read() with full URL
adapter.read("https://linear.app/1m-hyperdev/view/mcp-skills-issues-0d0359fabcf9")  # ❌ Wrong!

# EXPECTED: Should extract ID and call
adapter.read("mcp-skills-issues-0d0359fabcf9")  # ✅ Correct!
```

## Root Cause

In `src/mcp_ticketer/mcp/server/tools/ticket_tools.py`, the `else` blocks for handling non-routed cases had misleading comments saying "Use default adapter for plain IDs" but they didn't actually check if the input was a URL or a plain ID. They blindly passed the `ticket_id` parameter to the adapter, whether it was a URL or an ID.

**Affected Functions**:
1. `ticket_read()` - lines 341-344
2. `ticket_update()` - lines 443
3. `ticket_delete()` - lines 489
4. `ticket_assign()` - lines 716

## Fix

Added URL detection and ID extraction in all four functions:

```python
else:
    # Use default adapter for plain IDs OR URLs (without multi-platform routing)
    adapter = get_adapter()

    # If URL provided, extract ID for the adapter
    if is_url(ticket_id):
        # Extract ID from URL for default adapter
        adapter_type = type(adapter).__name__.lower().replace("adapter", "")
        extracted_id, error = extract_id_from_url(ticket_id, adapter_type=adapter_type)
        if error or not extracted_id:
            return {
                "status": "error",
                "error": f"Failed to extract ticket ID from URL: {ticket_id}. {error}"
            }
        ticket = await adapter.read(extracted_id)
    else:
        ticket = await adapter.read(ticket_id)
```

## Changes Summary

### Modified Files
- **src/mcp_ticketer/mcp/server/tools/ticket_tools.py**
  - Added import: `extract_id_from_url` from `....core.url_parser`
  - Fixed `ticket_read()` to extract ID from URLs before calling `adapter.read()`
  - Fixed `ticket_update()` to extract ID from URLs before calling `adapter.update()`
  - Fixed `ticket_delete()` to extract ID from URLs before calling `adapter.delete()`
  - Fixed `ticket_assign()` to extract ID from URLs before calling `adapter.read()` and `adapter.update()`
  - Fixed `ticket_assign()` comment handling to use extracted ID in `CommentModel`

### New Test File
- **tests/mcp/server/tools/test_ticket_url_without_router.py**
  - 9 comprehensive tests covering all four fixed functions
  - Tests URL handling for Linear, GitHub, JIRA, and Asana
  - Tests error handling for invalid URLs
  - Tests regression: plain IDs still work correctly
  - All tests PASSED ✅

## Test Results

### New Tests (URL Handling Without Router)
```
✅ test_read_with_linear_url_no_router
✅ test_read_with_github_url_no_router
✅ test_read_with_plain_id_no_router (regression test)
✅ test_update_with_linear_url_no_router
✅ test_delete_with_jira_url_no_router
✅ test_assign_with_asana_url_no_router
✅ test_assign_with_url_and_comment_no_router
✅ test_read_with_invalid_url_no_router (error handling)
✅ test_update_with_invalid_url_no_router (error handling)

9 passed in 3.33s
```

### Existing Tests (Regression Check)
```
✅ All 20 existing ticket_assign tests still pass
✅ No functionality broken
20 passed in 0.55s
```

## Behavior Changes

### Before Fix
- ❌ URLs without router → Error (adapter receives full URL)
- ✅ URLs with router → Works (router extracts ID)
- ✅ Plain IDs without router → Works (no extraction needed)

### After Fix
- ✅ URLs without router → **NOW WORKS** (ID extracted before adapter call)
- ✅ URLs with router → Works (router extracts ID, same as before)
- ✅ Plain IDs without router → Works (no regression)

## Impact

### User-Facing
- **Before**: Users had to configure multi-platform router OR use plain IDs
- **After**: Users can use URLs even with single-adapter setup
- **Improved UX**: More flexible, works as expected

### Developer-Facing
- No breaking changes
- All existing functionality preserved
- Better error messages when URL extraction fails

## Success Criteria ✅

- [x] URLs work correctly whether router is configured or not
- [x] IDs still work as before (no regression)
- [x] Helpful error if URL extraction fails
- [x] Same behavior across all 4 ticket tools
- [x] All tests pass
- [x] No breaking changes

## Technical Details

**Adapter Type Detection**:
```python
adapter_type = type(adapter).__name__.lower().replace("adapter", "")
# LinearAdapter → "linear"
# GitHubAdapter → "github"
# JiraAdapter → "jira"
# AsanaAdapter → "asana"
```

**URL Extraction**:
- Uses existing `extract_id_from_url()` from `core.url_parser`
- Auto-detects platform from URL domain
- Returns `(extracted_id, error_message)` tuple
- Handles all supported platforms

**Error Handling**:
- Returns clear error message with original URL
- Includes extraction error details
- Prevents adapter from receiving malformed input

## Files Modified

```
src/mcp_ticketer/mcp/server/tools/ticket_tools.py (4 functions fixed)
tests/mcp/server/tools/test_ticket_url_without_router.py (new test file)
```

## Lines of Code Impact

**Net LOC Delta**: +86 lines (test file) + 44 lines (implementation) = +130 total
- Implementation: +44 lines (URL extraction logic in 4 functions)
- Tests: +86 lines (comprehensive test coverage)
- Note: This is a bug fix, not a feature addition

## Related Issues

This fix resolves the issue where Linear view URLs (and other platform URLs) would fail when the router wasn't configured, even though the URL contained all necessary information to identify the ticket.

**Example Scenario**:
```python
# User has only Linear configured (no multi-platform router)
ticket_read("https://linear.app/team/view/ticket-id-123")
# Before: ERROR (URL passed to adapter)
# After: SUCCESS (ID extracted automatically)
```
