Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
python-version: ["3.12", "3.14"]

steps:
- uses: actions/checkout@v4
Expand Down
4 changes: 3 additions & 1 deletion src/example/comment/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ def __init__(self) -> None:
self._next_id = 1

def find_all_by_note(self, note_id: int, limit: int, offset: int) -> list[Comment]:
items = [c for c in self._store.values() if c.note_id == note_id]
items = sorted(
(c for c in self._store.values() if c.note_id == note_id), key=lambda c: c.id
)
return items[offset : offset + limit]

def find_by_id(self, comment_id: int) -> Comment | None:
Expand Down
30 changes: 15 additions & 15 deletions src/example/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,70 +77,70 @@ def create_mcp_server(settings: AppSettings | None = None) -> LocalMcpServer:
)

@server.tool("List notes with optional pagination.")
def list_notes(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg]
def list_notes(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
result = note_list.execute(ListNotesInput(limit=limit, offset=offset))
return [asdict(n) for n in result.items]

@server.tool("Get a single note by ID.")
def get_note(note_id: int) -> dict: # type: ignore[type-arg]
def get_note(note_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(note_get.execute(GetNoteInput(note_id=note_id)))

@server.tool("Create a new note.")
def create_note(title: str, body: str) -> dict: # type: ignore[type-arg]
def create_note(title: str, body: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(note_create.execute(CreateNoteInput(title=title, body=body)))

@server.tool("Update an existing note.")
def update_note(note_id: int, title: str, body: str) -> dict: # type: ignore[type-arg]
def update_note(note_id: int, title: str, body: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(note_update.execute(UpdateNoteInput(note_id=note_id, title=title, body=body)))

@server.tool("Delete a note by ID.")
def delete_note(note_id: int) -> dict: # type: ignore[type-arg]
def delete_note(note_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
note_delete.execute(DeleteNoteInput(note_id=note_id))
return {"deleted": True, "note_id": note_id}

@server.tool("List tags with optional pagination.")
def list_tags(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg]
def list_tags(limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
result = tag_list.execute(ListTagsInput(limit=limit, offset=offset))
return [asdict(t) for t in result.items]

@server.tool("Get a single tag by ID.")
def get_tag(tag_id: int) -> dict: # type: ignore[type-arg]
def get_tag(tag_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(tag_get.execute(GetTagInput(tag_id=tag_id)))

@server.tool("Create a new tag.")
def create_tag(name: str) -> dict: # type: ignore[type-arg]
def create_tag(name: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(tag_create.execute(CreateTagInput(name=name)))

@server.tool("Update an existing tag.")
def update_tag(tag_id: int, name: str) -> dict: # type: ignore[type-arg]
def update_tag(tag_id: int, name: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(tag_update.execute(UpdateTagInput(tag_id=tag_id, name=name)))

@server.tool("Delete a tag by ID.")
def delete_tag(tag_id: int) -> dict: # type: ignore[type-arg]
def delete_tag(tag_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
tag_delete.execute(DeleteTagInput(tag_id=tag_id))
return {"deleted": True, "tag_id": tag_id}

@server.tool("List comments for a note.")
def list_comments(note_id: int, limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg]
def list_comments(note_id: int, limit: int = 20, offset: int = 0) -> list[dict]: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
result = comment_list.execute(
ListCommentsInput(note_id=note_id, limit=limit, offset=offset)
)
return [asdict(c) for c in result.items]

@server.tool("Get a single comment by ID.")
def get_comment(comment_id: int) -> dict: # type: ignore[type-arg]
def get_comment(comment_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(comment_get.execute(GetCommentInput(comment_id=comment_id)))

@server.tool("Create a new comment on a note.")
def create_comment(note_id: int, body: str) -> dict: # type: ignore[type-arg]
def create_comment(note_id: int, body: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(comment_create.execute(CreateCommentInput(note_id=note_id, body=body)))

@server.tool("Update an existing comment.")
def update_comment(comment_id: int, body: str) -> dict: # type: ignore[type-arg]
def update_comment(comment_id: int, body: str) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
return asdict(comment_update.execute(UpdateCommentInput(comment_id=comment_id, body=body)))

@server.tool("Delete a comment by ID.")
def delete_comment(comment_id: int) -> dict: # type: ignore[type-arg]
def delete_comment(comment_id: int) -> dict: # type: ignore[type-arg] # reason: mcp tool handler type stubs do not support generic dict
comment_delete.execute(DeleteCommentInput(comment_id=comment_id))
return {"deleted": True, "comment_id": comment_id}

Expand Down
8 changes: 8 additions & 0 deletions src/nene2/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class AppSettings(BaseSettings):
db_user: str = ""
db_password: SecretStr = SecretStr("")

@field_validator("app_env")
@classmethod
def validate_app_env(cls, v: str) -> str:
allowed = {"local", "test", "production"}
if v not in allowed:
raise ValueError(f"app_env must be one of {allowed}")
return v

@field_validator("db_adapter")
@classmethod
def validate_adapter(cls, v: str) -> str:
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/export_openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Usage:
uv run export-openapi
python scripts/export_openapi.py
uv run python src/scripts/export_openapi.py
"""

import json
Expand Down
Loading