Skip to content

feat(tools): Add kind parameter to upload_document for template uploads - #70

Merged
hvenhlovskyi merged 3 commits into
mainfrom
SN-33253-v3-upload-template
Jul 23, 2026
Merged

feat(tools): Add kind parameter to upload_document for template uploads#70
hvenhlovskyi merged 3 commits into
mainfrom
SN-33253-v3-upload-template

Conversation

@hvenhlovskyi

@hvenhlovskyi hvenhlovskyi commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Let upload_document optionally store the upload as a reusable template via a kind parameter ("document" | "template"), instead of a separate tool. kind defaults to "document", so plain document upload is unchanged; with kind="template" the returned document_id is a template id and next_steps switch to the template follow-ups (create_from_template / create_embedded_editor).

Because adding a parameter changes a released tool contract, it ships as a new upload_document v3.0 registration in tools/signnow_v3.py; the v2.0 contract stays frozen and unchanged. FastMCP serves the highest version by default, so clients get the new contract transparently while a client pinning v2.0 keeps the old one.

The kind selector lives only at the tool boundary; it maps to make_template = kind == "template" before calling the shared business logic. The signnow_client / CreateDocumentFromUrlRequest layers keep make_template, since that is the actual SignNow API field.

Establish the governance rule in AGENTS.md: every tool contract change or new tool goes through the latest MCP version only (currently v3.0), never edits v1.0/v2.0 in place. CLAUDE.md now points to AGENTS.md and surfaces the rule.

Supporting changes:

  • signnow_client.upload_document / CreateDocumentFromUrlRequest gain make_template (and an optional name for URL uploads); the flag is omitted when false so the API's PHP !empty() check can't treat a "false" string as truthy.
  • URL-upload filenames are validated leniently — a caller-supplied display name need not carry an extension, since SignNow types the fetched file server-side.
  • signnow101 skill and README document kind='template'; tests cover the document / template / URL paths and the shared MCP-resource upload helper.

Let upload_document optionally store the upload as a reusable template via a
make_template flag, instead of a separate tool. make_template defaults to
false, so plain document upload is unchanged; when true the returned
document_id is a template id and next_steps switch to the template follow-ups
(create_from_template / create_embedded_editor).

Because adding a parameter changes a released tool contract, the flag ships as
a new upload_document v3.0 registration in tools/signnow_v3.py; the v2.0
contract stays frozen and unchanged. FastMCP serves the highest version by
default, so clients get the new flag transparently while a client pinning v2.0
keeps the old contract.

Establish the governance rule in AGENTS.md: every tool contract change or new
tool goes through the latest MCP version only (currently v3.0), never edits
v1.0/v2.0 in place. CLAUDE.md now points to AGENTS.md and surfaces the rule.

Supporting changes:
- signnow_client.upload_document / CreateDocumentFromUrlRequest gain make_template
  (and an optional name for URL uploads); the flag is omitted when false so the
  API's PHP !empty() check can't treat a "false" string as truthy.
- URL-upload filenames are validated leniently — a caller-supplied display name
  need not carry an extension, since SignNow types the fetched file server-side.
- signnow101 skill and README document the flag; tests cover the make_template
  document/template/URL paths and the shared MCP-resource upload helper.
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/signnow_client/client_documents.py (100%)
  • src/signnow_client/models/templates_and_documents.py (100%)
  • src/sn_mcp_server/tools/init.py (100%)
  • src/sn_mcp_server/tools/document.py (100%)
  • src/sn_mcp_server/tools/models.py (100%)
  • src/sn_mcp_server/tools/signnow.py (94.7%): Missing lines 1056
  • src/sn_mcp_server/tools/signnow_v3.py (90.9%): Missing lines 140,142

Summary

  • Total: 112 lines
  • Missing: 3 lines
  • Coverage: 97%

src/sn_mcp_server/tools/signnow.py

Lines 1052-1060

  1052             raise ValueError("Provide exactly one of resource_uri, file_path, or file_url — not multiple")
  1053         if provided == 0:
  1054             raise ValueError("Provide one of: resource_uri, file_path, or file_url")
  1055 
! 1056         resource_bytes, filename = await _resolve_upload_resource(ctx, resource_uri, filename)
  1057 
  1058         # H-3: Run synchronous _upload_document off the async event loop
  1059         return await asyncio.to_thread(
  1060             _upload_document,

src/sn_mcp_server/tools/signnow_v3.py

Lines 136-146

  136 
  137         # Validate mutually-exclusive source inputs before any I/O
  138         provided = sum(x is not None for x in (resource_uri, file_path, file_url))
  139         if provided > 1:
! 140             raise ValueError("Provide exactly one of resource_uri, file_path, or file_url — not multiple")
  141         if provided == 0:
! 142             raise ValueError("Provide one of: resource_uri, file_path, or file_url")
  143 
  144         resource_bytes, filename = await _resolve_upload_resource(ctx, resource_uri, filename)
  145 
  146         # Run synchronous _upload_document off the async event loop

@hvenhlovskyi
hvenhlovskyi marked this pull request as draft July 14, 2026 13:10
Swap the boolean make_template argument on the v3.0 upload_document tool for a
kind parameter accepting "document" (default) or "template". This reads better
as an agent-facing contract than a boolean and leaves room for future upload
kinds without another flag.

kind lives only at the tool boundary (signnow_v3.py); it maps to
make_template=(kind == "template") before calling the business logic. The
signnow_client / CreateDocumentFromUrlRequest layers keep make_template
unchanged, since that is the actual SignNow API field.

Since the make_template flag was only introduced earlier in this same branch
and never released, this edits the v3.0 contract in place rather than adding a
new version. Update the tool description, docstring, and UploadDocumentResponse
field descriptions, plus the README and signnow101 skill, to speak in terms of
kind. Existing tests for the shared upload helper still exercise make_template
at the business-logic layer and pass unchanged.
@hvenhlovskyi
hvenhlovskyi marked this pull request as ready for review July 20, 2026 13:39
@hvenhlovskyi
hvenhlovskyi requested a review from Copilot July 20, 2026 13:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for uploading a file as a reusable template via the existing upload_document tool flow, implemented as a new v3.0 tool registration while keeping the v2.0 contract available.

Changes:

  • Introduces upload_document v3.0 registration with a new selector (kind=document|template) and wires it into tool registration/version invariants.
  • Extends upload plumbing to support template uploads end-to-end (tool → business logic → SignNow client), including omission of make_template when false to avoid PHP !empty() pitfalls.
  • Adds/updates unit, integration, and API tests for resource/URL/local uploads and template-specific follow-ups; updates docs/skills/README accordingly.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/sn_mcp_server/tools/test_upload_resource.py New unit coverage for _resolve_upload_resource resource attachment handling.
tests/unit/sn_mcp_server/tools/test_document.py Expands upload tests (URL naming rules, make_template behavior, template next_steps).
tests/unit/sn_mcp_server/test_tool_versions.py Adds v3/v2 coexistence invariant for contract changes (upload_document).
tests/integration/test_upload_document.py Integration coverage for template uploads over resource bytes and URL paths.
tests/api/test_upload_document.py API-layer assertions for make_template omission/presence and URL request optional fields.
src/sn_mcp_server/tools/signnow.py Extracts resource attachment resolution into _resolve_upload_resource; keeps v2 upload orchestration.
src/sn_mcp_server/tools/signnow_v3.py New v3.0 tool registrations, including upload_document with `kind=document
src/sn_mcp_server/tools/models.py Clarifies UploadDocumentResponse semantics for template uploads.
src/sn_mcp_server/tools/document.py Refactors upload logic, adds template next_steps/guidance, and supports passing make_template/name to client.
src/sn_mcp_server/tools/init.py Registers v3 tool binder alongside v1 and v2.
src/sn_mcp_server/skills/signnow101.md Documents template upload flow via upload_document(kind='template').
src/signnow_client/models/templates_and_documents.py Extends CreateDocumentFromUrlRequest with make_template and name.
src/signnow_client/client_documents.py Adds make_template to multipart upload and uses exclude_none=True for URL payloads.
README.md Updates upload_document docs to describe template uploads via kind='template'.
Makefile Adds uv-up convenience target.
CLAUDE.md New pointer file directing governance rules to AGENTS.md.
AGENTS.md Adds explicit tool-versioning governance rule and updates last-updated date.

Comment thread src/sn_mcp_server/tools/document.py Outdated
Comment on lines 231 to 235
@@ -135,70 +235,85 @@ def _upload_document(
raise ValueError("Provide one of: resource_uri, file_path, or file_url")
Comment thread src/sn_mcp_server/tools/document.py
Comment thread src/sn_mcp_server/tools/signnow_v3.py
@hvenhlovskyi hvenhlovskyi changed the title feat(tools): Add make_template flag to upload_document feat(tools): Add kind parameter to upload_document for template uploads Jul 20, 2026
…eview notes

Add tests/unit/sn_mcp_server/tools/test_upload_document_v3.py exercising the
v3.0 upload_document tool body end to end (token, client, and resource
resolution patched): kind='template' forwards make_template=True and returns
template follow-ups, while the default 'document' forwards make_template=False.
This covers the tool-boundary mapping the shared _upload_document helper tests
don't reach.

Address PR review comments on document.py:
- _upload() validation messages named resource_uri, but the helper takes
  resource_bytes; rename them so direct callers/tests see accurate arguments.
- _upload_document() filename docstring said "document" only; reword to entity
  (document or template) and note URL uploads transmit an explicit name.

Update the corresponding no-source test assertion to the resource_bytes wording.
@hvenhlovskyi
hvenhlovskyi merged commit 2a61f75 into main Jul 23, 2026
1 check passed
@hvenhlovskyi
hvenhlovskyi deleted the SN-33253-v3-upload-template branch July 23, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants