nocfo-cli 1.5.2__tar.gz → 1.5.3__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.
Files changed (72) hide show
  1. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/PKG-INFO +1 -1
  2. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/pyproject.toml +1 -1
  3. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/relation.py +0 -50
  4. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/common.py +0 -19
  5. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/bookkeeping/document.py +3 -25
  6. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/common.py +0 -6
  7. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/invoicing/sales_invoice.py +12 -2
  8. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/LICENSE +0 -0
  9. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/README.md +0 -0
  10. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/__init__.py +0 -0
  11. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/api_client.py +0 -0
  12. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/__init__.py +0 -0
  13. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/app.py +0 -0
  14. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/__init__.py +0 -0
  15. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/_helpers.py +0 -0
  16. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/accounts.py +0 -0
  17. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/auth.py +0 -0
  18. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/businesses.py +0 -0
  19. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/contacts.py +0 -0
  20. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/documents.py +0 -0
  21. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/files.py +0 -0
  22. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/invoices.py +0 -0
  23. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/products.py +0 -0
  24. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/purchase_invoices.py +0 -0
  25. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/reports.py +0 -0
  26. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/tags.py +0 -0
  27. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/commands/user.py +0 -0
  28. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/context.py +0 -0
  29. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/cli/output.py +0 -0
  30. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/config.py +0 -0
  31. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/__init__.py +0 -0
  32. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/auth.py +0 -0
  33. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/__init__.py +0 -0
  34. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/__init__.py +0 -0
  35. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/account.py +0 -0
  36. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/document.py +0 -0
  37. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/header.py +0 -0
  38. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/bookkeeping/tag_file.py +0 -0
  39. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/client.py +0 -0
  40. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/confirmation.py +0 -0
  41. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/constants/__init__.py +0 -0
  42. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/constants/docs.py +0 -0
  43. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/errors.py +0 -0
  44. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/instructions.py +0 -0
  45. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/invoicing/__init__.py +0 -0
  46. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/invoicing/contact.py +0 -0
  47. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/invoicing/product.py +0 -0
  48. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/invoicing/purchase_invoice.py +0 -0
  49. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/invoicing/sales_invoice.py +0 -0
  50. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/reporting/__init__.py +0 -0
  51. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/reporting/report.py +0 -0
  52. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/runtime.py +0 -0
  53. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/__init__.py +0 -0
  54. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/bookkeeping/__init__.py +0 -0
  55. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/bookkeeping/account.py +0 -0
  56. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/bookkeeping/header.py +0 -0
  57. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/bookkeeping/tag_file.py +0 -0
  58. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/constants/__init__.py +0 -0
  59. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/constants/docs.py +0 -0
  60. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/invoicing/__init__.py +0 -0
  61. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/invoicing/contact.py +0 -0
  62. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/invoicing/product.py +0 -0
  63. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/invoicing/purchase_invoice.py +0 -0
  64. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/reporting/__init__.py +0 -0
  65. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schema/reporting/report.py +0 -0
  66. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/schemas.py +0 -0
  67. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/curated/utils.py +0 -0
  68. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/error_handling.py +0 -0
  69. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/http_error_capture.py +0 -0
  70. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/middleware.py +0 -0
  71. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/search.py +0 -0
  72. {nocfo_cli-1.5.2 → nocfo_cli-1.5.3}/src/nocfo_toolkit/mcp/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nocfo-cli
3
- Version: 1.5.2
3
+ Version: 1.5.3
4
4
  Summary: NoCFO CLI, MCP server, and Cursor skill toolkit.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "nocfo-cli"
7
- version = "1.5.2"
7
+ version = "1.5.3"
8
8
  description = "NoCFO CLI, MCP server, and Cursor skill toolkit."
9
9
  authors = ["NoCFO"]
10
10
  readme = "README.md"
@@ -8,12 +8,10 @@ from fastmcp.tools import tool
8
8
  from nocfo_toolkit.mcp.curated.confirmation import confirm_mutation
9
9
  from nocfo_toolkit.mcp.curated.runtime import business_slug, get_client
10
10
  from nocfo_toolkit.mcp.curated.bookkeeping.document import document_by_number
11
- from nocfo_toolkit.mcp.curated.errors import raise_tool_error
12
11
  from nocfo_toolkit.mcp.curated.schemas import (
13
12
  DeletedResponse,
14
13
  DocumentRelationCreateInput,
15
14
  DocumentRelationIdInput,
16
- DocumentRelationUpdateInput,
17
15
  EntryListInput,
18
16
  ListEnvelope,
19
17
  RelationSummary,
@@ -97,36 +95,6 @@ async def bookkeeping_document_relation_create(
97
95
  return dump_model_from_backend(RelationSummary, result)
98
96
 
99
97
 
100
- @tool(
101
- name="bookkeeping_document_relation_update",
102
- description="Update a relation listed for the same document context.",
103
- )
104
- async def bookkeeping_document_relation_update(
105
- params: DocumentRelationUpdateInput,
106
- ) -> dict[str, Any]:
107
- args = params
108
- slug = await business_slug(args.business)
109
- document = await document_by_number(slug, args.document_number)
110
- payload = await _resolve_relation_update_payload(slug, args.payload.model_dump())
111
- path = f"/v1/business/{slug}/document/{document['id']}/relation/{args.relation_id}/"
112
- await confirm_mutation(
113
- business=slug,
114
- tool_name="bookkeeping_document_relation_update",
115
- target_resource={
116
- "type": "document_relation",
117
- "id": args.relation_id,
118
- },
119
- parameters=payload,
120
- )
121
- result = await get_client().request(
122
- "PATCH",
123
- path,
124
- json_body=payload,
125
- business_slug=slug,
126
- )
127
- return dump_model_from_backend(RelationSummary, result)
128
-
129
-
130
98
  @tool(
131
99
  name="bookkeeping_document_relation_delete",
132
100
  description="Delete a relation listed for the same document context.",
@@ -152,21 +120,3 @@ async def bookkeeping_document_relation_delete(
152
120
  business_slug=slug,
153
121
  )
154
122
  return dump_model(DeletedResponse(relation_id=args.relation_id))
155
-
156
-
157
- async def _resolve_relation_update_payload(
158
- slug: str, raw_payload: dict[str, Any]
159
- ) -> dict[str, Any]:
160
- payload = {key: value for key, value in raw_payload.items() if value is not None}
161
- related_document_number = payload.pop("related_document_number", None)
162
- if related_document_number is not None:
163
- related = await document_by_number(slug, str(related_document_number))
164
- payload["related_document"] = related["id"]
165
- if not payload:
166
- raise_tool_error(
167
- "invalid_request",
168
- "At least one mutable relation field is required.",
169
- "Provide one or more of related_document_number, role, or type.",
170
- status_code=400,
171
- )
172
- return payload
@@ -5,14 +5,12 @@ from __future__ import annotations
5
5
  from typing import Any
6
6
 
7
7
  from fastmcp.tools import tool
8
- from nocfo_toolkit.mcp.curated.confirmation import confirm_mutation
9
8
  from nocfo_toolkit.mcp.curated.runtime import get_client
10
9
  from nocfo_toolkit.mcp.curated.schemas import (
11
10
  BusinessContextInput,
12
11
  BusinessSummary,
13
12
  ListEnvelope,
14
13
  PaginationInput,
15
- PayloadOnlyInput,
16
14
  ResolvedBusiness,
17
15
  UserSummary,
18
16
  dump_model,
@@ -56,20 +54,3 @@ async def common_accessible_businesses_list(
56
54
  async def common_user_retrieve() -> dict[str, Any]:
57
55
  payload = await get_client().request("GET", "/v1/user/")
58
56
  return dump_model_from_backend(UserSummary, payload)
59
-
60
-
61
- @tool(
62
- name="common_user_update",
63
- description="Update basic authenticated user profile fields.",
64
- )
65
- async def common_user_update(params: PayloadOnlyInput) -> dict[str, Any]:
66
- args = params
67
- path = "/v1/user/"
68
- await confirm_mutation(
69
- business="current",
70
- tool_name="common_user_update",
71
- target_resource={"type": "user", "id": "current"},
72
- parameters=args.payload,
73
- )
74
- result = await get_client().request("PATCH", path, json_body=args.payload)
75
- return dump_model_from_backend(UserSummary, result)
@@ -13,7 +13,6 @@ from nocfo_toolkit.mcp.curated.schema.common import (
13
13
  DocumentAction,
14
14
  DocumentSide,
15
15
  RelationRole,
16
- StrictModel,
17
16
  RelationType,
18
17
  enum_or_str,
19
18
  tool_handle,
@@ -197,28 +196,7 @@ class DocumentRelationCreateInput(DocumentNumberInput):
197
196
 
198
197
  class DocumentRelationIdInput(DocumentNumberInput):
199
198
  relation_id: int = Field(
200
- description="Relation ID from bookkeeping_document_relations_list. Use bookkeeping_document_relation_update or bookkeeping_document_relation_delete for this ID."
201
- )
202
-
203
-
204
- class DocumentRelationUpdatePayload(StrictModel):
205
- related_document_number: str | None = Field(
206
- default=None,
207
- description="Document number of the other bookkeeping document in the relation.",
208
- )
209
- role: enum_or_str(RelationRole) | None = Field(
210
- default=None,
211
- description="Whether the current document is the accrual or settlement side of the relation.",
212
- )
213
- type: enum_or_str(RelationType) | None = Field(
214
- default=None,
215
- description="Type/category shown for this record.",
216
- )
217
-
218
-
219
- class DocumentRelationUpdateInput(DocumentRelationIdInput):
220
- payload: DocumentRelationUpdatePayload = Field(
221
- description="Relation update payload. Supported fields: related_document_number, role, and type."
199
+ description="Relation ID from bookkeeping_document_relations_list. Use bookkeeping_document_relation_delete for this ID."
222
200
  )
223
201
 
224
202
 
@@ -434,12 +412,12 @@ class EntrySummary(AgentModel):
434
412
  class RelationSummary(AgentModel):
435
413
  tool_handle: str | None = Field(
436
414
  default=None,
437
- description="Copy this value from bookkeeping_document_relations_list.items[].tool_handle and pass it unchanged to bookkeeping_document_relation_update or bookkeeping_document_relation_delete.",
415
+ description="Copy this value from bookkeeping_document_relations_list.items[].tool_handle and pass it unchanged to bookkeeping_document_relation_delete.",
438
416
  )
439
417
  relation_id: int | None = Field(
440
418
  default=None,
441
419
  validation_alias=AliasChoices("relation_id", "id"),
442
- description="Relation ID. Use bookkeeping_document_relation_update or bookkeeping_document_relation_delete for this ID.",
420
+ description="Relation ID. Use bookkeeping_document_relation_delete for this ID.",
443
421
  )
444
422
  related_document_handle: str | None = Field(
445
423
  default=None,
@@ -385,12 +385,6 @@ class ExactResourceInput(BusinessContextInput):
385
385
  return self
386
386
 
387
387
 
388
- class PayloadOnlyInput(StrictModel):
389
- payload: dict[str, Any] = Field(
390
- description="Fields to update on the current user profile."
391
- )
392
-
393
-
394
388
  class IdentifierInput(BusinessContextInput):
395
389
  identifier: str = Field(
396
390
  description="Identifier for this resource. Prefer the exact value named in the tool description."
@@ -19,15 +19,23 @@ from nocfo_toolkit.mcp.curated.schema.common import (
19
19
 
20
20
 
21
21
  def _derive_invoice_next_action(
22
- *, status_raw: str | None, is_sendable: bool | None, last_delivery_at: str | None
22
+ *,
23
+ status_raw: str | None,
24
+ is_sendable: bool | None,
25
+ last_delivery_at: str | None,
26
+ invoice_number: int | str | None = None,
23
27
  ) -> tuple[str | None, str | None]:
24
28
  status = str(status_raw).strip().upper() if status_raw is not None else ""
25
29
  can_send = bool(is_sendable)
26
30
 
27
31
  if status == "DRAFT":
32
+ if invoice_number is None:
33
+ identifier_hint = "Use `tool_handle` from this response because `invoice_number` is assigned only after acceptance."
34
+ else:
35
+ identifier_hint = "You can use either `invoice_number` or `tool_handle`."
28
36
  return (
29
37
  "accept",
30
- "Invoice is in DRAFT status. Next step: call invoicing_sales_invoice_action with action=accept.",
38
+ f"Invoice is in DRAFT status. Next step: call invoicing_sales_invoice_action with action=accept. {identifier_hint}",
31
39
  )
32
40
 
33
41
  if can_send and not last_delivery_at:
@@ -286,6 +294,7 @@ class SalesInvoiceSummary(AgentModel):
286
294
  status_raw=self.status,
287
295
  is_sendable=self.is_sendable,
288
296
  last_delivery_at=self.last_delivery_at,
297
+ invoice_number=self.invoice_number,
289
298
  )
290
299
  if self.next_action is None:
291
300
  self.next_action = next_action
@@ -356,6 +365,7 @@ class SalesInvoiceListItem(AgentModel):
356
365
  status_raw=data.get("status"),
357
366
  is_sendable=data.get("is_sendable"),
358
367
  last_delivery_at=data.get("last_delivery_at"),
368
+ invoice_number=data.get("invoice_number"),
359
369
  )
360
370
  if "next_action" not in data:
361
371
  data["next_action"] = next_action
File without changes
File without changes