Skip to content

Commit d8243d3

Browse files
authored
Merge pull request #4 from disler/codex/list-available-specs
Implement pocket_list_ids feature
2 parents fb1dc69 + d62a9d6 commit d8243d3

File tree

4 files changed

+175
-1
lines changed

4 files changed

+175
-1
lines changed

src/mcp_server_pocket_pick/modules/data_types.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,10 @@ class PocketItem(BaseModel):
6464
id: str
6565
created: datetime
6666
text: str
67-
tags: List[str]
67+
tags: List[str]
68+
69+
70+
class ListIdsCommand(BaseModel):
71+
tags: List[str] = []
72+
limit: int = 100
73+
db_path: Path = DEFAULT_SQLITE_DATABASE_PATH
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import sqlite3
2+
from typing import List
3+
import logging
4+
from ..data_types import ListIdsCommand
5+
from ..init_db import init_db, normalize_tags
6+
7+
logger = logging.getLogger(__name__)
8+
9+
def list_ids(command: ListIdsCommand) -> List[str]:
10+
"""List all item IDs in the database, optionally filtered by tags."""
11+
normalized_tags = normalize_tags(command.tags) if command.tags else []
12+
13+
db = init_db(command.db_path)
14+
15+
try:
16+
if normalized_tags:
17+
placeholders = ', '.join(['?'] * len(normalized_tags))
18+
query = f"""
19+
SELECT id
20+
FROM POCKET_PICK
21+
WHERE id IN (
22+
SELECT id
23+
FROM POCKET_PICK
24+
WHERE (
25+
SELECT COUNT(*)
26+
FROM json_each(tags)
27+
WHERE json_each.value IN ({placeholders})
28+
) = ?
29+
)
30+
ORDER BY created DESC
31+
LIMIT ?
32+
"""
33+
params = [*normalized_tags, len(normalized_tags), command.limit]
34+
else:
35+
query = """
36+
SELECT id
37+
FROM POCKET_PICK
38+
ORDER BY created DESC
39+
LIMIT ?
40+
"""
41+
params = [command.limit]
42+
43+
cursor = db.execute(query, params)
44+
results = [row[0] for row in cursor.fetchall()]
45+
return results
46+
except Exception as e:
47+
logger.error(f"Error listing ids: {e}")
48+
raise
49+
finally:
50+
db.close()

src/mcp_server_pocket_pick/server.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
FindCommand,
2121
ListCommand,
2222
ListTagsCommand,
23+
ListIdsCommand,
2324
RemoveCommand,
2425
GetCommand,
2526
BackupCommand,
@@ -30,6 +31,7 @@
3031
from .modules.functionality.find import find
3132
from .modules.functionality.list import list_items
3233
from .modules.functionality.list_tags import list_tags
34+
from .modules.functionality.list_ids import list_ids
3335
from .modules.functionality.remove import remove
3436
from .modules.functionality.get import get
3537
from .modules.functionality.backup import backup
@@ -67,6 +69,11 @@ class PocketListTags(BaseModel):
6769
limit: int = 1000
6870
db: str = str(DEFAULT_SQLITE_DATABASE_PATH)
6971

72+
class PocketListIds(BaseModel):
73+
tags: List[str] = []
74+
limit: int = 100
75+
db: str = str(DEFAULT_SQLITE_DATABASE_PATH)
76+
7077
class PocketRemove(BaseModel):
7178
id: str
7279
db: str = str(DEFAULT_SQLITE_DATABASE_PATH)
@@ -90,6 +97,7 @@ class PocketTools(str, Enum):
9097
FIND = "pocket_find"
9198
LIST = "pocket_list"
9299
LIST_TAGS = "pocket_list_tags"
100+
LIST_IDS = "pocket_list_ids"
93101
REMOVE = "pocket_remove"
94102
GET = "pocket_get"
95103
BACKUP = "pocket_backup"
@@ -138,6 +146,11 @@ async def list_tools() -> list[Tool]:
138146
description="List all tags in your pocket pick database with their counts",
139147
inputSchema=PocketListTags.schema(),
140148
),
149+
Tool(
150+
name=PocketTools.LIST_IDS,
151+
description="List all item IDs in your pocket pick database, optionally filtered by tags",
152+
inputSchema=PocketListIds.schema(),
153+
),
141154
Tool(
142155
name=PocketTools.REMOVE,
143156
description="Remove an item from your pocket pick database by ID",
@@ -285,6 +298,25 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
285298
type="text",
286299
text="\n".join(output)
287300
)]
301+
302+
case PocketTools.LIST_IDS:
303+
command = ListIdsCommand(
304+
tags=arguments.get("tags", []),
305+
limit=arguments.get("limit", 100),
306+
db_path=db_path
307+
)
308+
results = list_ids(command)
309+
310+
if not results:
311+
return [TextContent(
312+
type="text",
313+
text="No item IDs found."
314+
)]
315+
316+
return [TextContent(
317+
type="text",
318+
text="\n".join(results)
319+
)]
288320

289321
case PocketTools.REMOVE:
290322
command = RemoveCommand(
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pytest
2+
import tempfile
3+
import os
4+
from pathlib import Path
5+
from ...modules.data_types import AddCommand, ListIdsCommand
6+
from ...modules.functionality.add import add
7+
from ...modules.functionality.list_ids import list_ids
8+
9+
@pytest.fixture
10+
def temp_db_path():
11+
fd, path = tempfile.mkstemp()
12+
os.close(fd)
13+
yield Path(path)
14+
if os.path.exists(path):
15+
os.unlink(path)
16+
17+
@pytest.fixture
18+
def populated_db(temp_db_path):
19+
items = [
20+
{"id": "python-1", "text": "Python programming is fun", "tags": ["python", "programming", "fun"]},
21+
{"id": "sql-1", "text": "SQL databases are powerful", "tags": ["sql", "database", "programming"]},
22+
{"id": "testing-1", "text": "Testing code is important", "tags": ["testing", "code", "programming"]},
23+
{"id": "regex-1", "text": "Regular expressions can be complex", "tags": ["regex", "programming", "advanced"]},
24+
{"id": "learning-1", "text": "Learning new technologies is exciting", "tags": ["learning", "technology", "fun"]},
25+
]
26+
27+
for item in items:
28+
command = AddCommand(
29+
id=item["id"],
30+
text=item["text"],
31+
tags=item["tags"],
32+
db_path=temp_db_path,
33+
)
34+
add(command)
35+
36+
return temp_db_path
37+
38+
def test_list_ids_all(populated_db):
39+
command = ListIdsCommand(
40+
limit=10,
41+
db_path=populated_db,
42+
)
43+
44+
results = list_ids(command)
45+
46+
assert len(results) == 5
47+
for expected in [
48+
"python-1",
49+
"sql-1",
50+
"testing-1",
51+
"regex-1",
52+
"learning-1",
53+
]:
54+
assert expected in results
55+
56+
def test_list_ids_with_tags(populated_db):
57+
command = ListIdsCommand(
58+
tags=["programming"],
59+
limit=10,
60+
db_path=populated_db,
61+
)
62+
63+
results = list_ids(command)
64+
65+
assert len(results) == 4
66+
for expected in ["python-1", "sql-1", "testing-1", "regex-1"]:
67+
assert expected in results
68+
69+
def test_list_ids_limit(populated_db):
70+
command = ListIdsCommand(
71+
limit=2,
72+
db_path=populated_db,
73+
)
74+
75+
results = list_ids(command)
76+
77+
assert len(results) == 2
78+
79+
def test_list_ids_empty_db(temp_db_path):
80+
command = ListIdsCommand(
81+
db_path=temp_db_path,
82+
)
83+
84+
results = list_ids(command)
85+
86+
assert len(results) == 0

0 commit comments

Comments
 (0)