+4
-8
src/tangled_mcp/server.py
+4
-8
src/tangled_mcp/server.py
···
53
limit: Annotated[
54
int, Field(ge=1, le=100, description="maximum number of branches to return")
55
] = 50,
56
-
cursor: Annotated[str | None, Field(description="pagination cursor")] = None,
57
) -> ListBranchesResult:
58
"""list branches for a repository
59
60
Args:
61
repo: repository identifier in 'owner/repo' format (e.g., 'zzstoatzz/tangled-mcp')
62
limit: maximum number of branches to return (1-100)
63
-
cursor: optional pagination cursor
64
65
Returns:
66
-
list of branches with optional cursor for pagination
67
"""
68
# resolve owner/repo to (knot, did/repo)
69
knot, repo_id = _tangled.resolve_repo_identifier(repo)
70
-
response = _tangled.list_branches(knot, repo_id, limit, cursor)
71
72
return ListBranchesResult.from_api_response(response)
73
···
188
limit: Annotated[
189
int, Field(ge=1, le=100, description="maximum number of issues to return")
190
] = 20,
191
-
cursor: Annotated[str | None, Field(description="pagination cursor")] = None,
192
) -> ListIssuesResult:
193
"""list issues for a repository
194
195
Args:
196
repo: repository identifier in 'owner/repo' format
197
limit: maximum number of issues to return (1-100)
198
-
cursor: optional pagination cursor
199
200
Returns:
201
-
ListIssuesResult with list of issues and optional cursor
202
"""
203
# resolve owner/repo to (knot, did/repo)
204
_, repo_id = _tangled.resolve_repo_identifier(repo)
205
# list_repo_issues doesn't need knot (queries atproto records, not XRPC)
206
-
response = _tangled.list_repo_issues(repo_id, limit, cursor)
207
208
return ListIssuesResult.from_api_response(response)
209
···
53
limit: Annotated[
54
int, Field(ge=1, le=100, description="maximum number of branches to return")
55
] = 50,
56
) -> ListBranchesResult:
57
"""list branches for a repository
58
59
Args:
60
repo: repository identifier in 'owner/repo' format (e.g., 'zzstoatzz/tangled-mcp')
61
limit: maximum number of branches to return (1-100)
62
63
Returns:
64
+
list of branches
65
"""
66
# resolve owner/repo to (knot, did/repo)
67
knot, repo_id = _tangled.resolve_repo_identifier(repo)
68
+
response = _tangled.list_branches(knot, repo_id, limit, cursor=None)
69
70
return ListBranchesResult.from_api_response(response)
71
···
186
limit: Annotated[
187
int, Field(ge=1, le=100, description="maximum number of issues to return")
188
] = 20,
189
) -> ListIssuesResult:
190
"""list issues for a repository
191
192
Args:
193
repo: repository identifier in 'owner/repo' format
194
limit: maximum number of issues to return (1-100)
195
196
Returns:
197
+
ListIssuesResult with list of issues
198
"""
199
# resolve owner/repo to (knot, did/repo)
200
_, repo_id = _tangled.resolve_repo_identifier(repo)
201
# list_repo_issues doesn't need knot (queries atproto records, not XRPC)
202
+
response = _tangled.list_repo_issues(repo_id, limit, cursor=None)
203
204
return ListIssuesResult.from_api_response(response)
205
+2
-4
src/tangled_mcp/types/_branches.py
+2
-4
src/tangled_mcp/types/_branches.py
···
16
"""result of listing branches"""
17
18
branches: list[BranchInfo]
19
-
cursor: str | None = None
20
21
@classmethod
22
def from_api_response(cls, response: dict[str, Any]) -> "ListBranchesResult":
···
28
"branches": [
29
{"reference": {"name": "main", "hash": "abc123"}},
30
...
31
-
],
32
-
"cursor": "optional_cursor"
33
}
34
35
Returns:
···
46
)
47
)
48
49
-
return cls(branches=branches, cursor=response.get("cursor"))
···
16
"""result of listing branches"""
17
18
branches: list[BranchInfo]
19
20
@classmethod
21
def from_api_response(cls, response: dict[str, Any]) -> "ListBranchesResult":
···
27
"branches": [
28
{"reference": {"name": "main", "hash": "abc123"}},
29
...
30
+
]
31
}
32
33
Returns:
···
44
)
45
)
46
47
+
return cls(branches=branches)
+2
-4
src/tangled_mcp/types/_issues.py
+2
-4
src/tangled_mcp/types/_issues.py
···
61
"""result of listing issues"""
62
63
issues: list[IssueInfo]
64
-
cursor: str | None = None
65
66
@classmethod
67
def from_api_response(cls, response: dict[str, Any]) -> "ListIssuesResult":
···
80
"createdAt": "..."
81
},
82
...
83
-
],
84
-
"cursor": "optional_cursor"
85
}
86
87
Returns:
88
ListIssuesResult with parsed issues
89
"""
90
issues = [IssueInfo(**issue_data) for issue_data in response.get("issues", [])]
91
-
return cls(issues=issues, cursor=response.get("cursor"))
···
61
"""result of listing issues"""
62
63
issues: list[IssueInfo]
64
65
@classmethod
66
def from_api_response(cls, response: dict[str, Any]) -> "ListIssuesResult":
···
79
"createdAt": "..."
80
},
81
...
82
+
]
83
}
84
85
Returns:
86
ListIssuesResult with parsed issues
87
"""
88
issues = [IssueInfo(**issue_data) for issue_data in response.get("issues", [])]
89
+
return cls(issues=issues)
-12
tests/test_types.py
-12
tests/test_types.py
···
58
{"reference": {"name": "main", "hash": "abc123"}},
59
{"reference": {"name": "dev", "hash": "def456"}},
60
],
61
-
"cursor": "next_page",
62
}
63
64
result = ListBranchesResult.from_api_response(response)
···
68
assert result.branches[0].sha == "abc123"
69
assert result.branches[1].name == "dev"
70
assert result.branches[1].sha == "def456"
71
-
assert result.cursor == "next_page"
72
-
73
-
def test_handles_missing_cursor(self):
74
-
"""cursor is optional in API response"""
75
-
response = {"branches": [{"reference": {"name": "main", "hash": "abc123"}}]}
76
-
77
-
result = ListBranchesResult.from_api_response(response)
78
-
79
-
assert len(result.branches) == 1
80
-
assert result.cursor is None
81
82
def test_handles_empty_branches(self):
83
"""handles empty branches list"""
···
86
result = ListBranchesResult.from_api_response(response)
87
88
assert result.branches == []
89
-
assert result.cursor is None
···
58
{"reference": {"name": "main", "hash": "abc123"}},
59
{"reference": {"name": "dev", "hash": "def456"}},
60
],
61
}
62
63
result = ListBranchesResult.from_api_response(response)
···
67
assert result.branches[0].sha == "abc123"
68
assert result.branches[1].name == "dev"
69
assert result.branches[1].sha == "def456"
70
71
def test_handles_empty_branches(self):
72
"""handles empty branches list"""
···
75
result = ListBranchesResult.from_api_response(response)
76
77
assert result.branches == []