sn-architecture — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited sn-architecture (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This server is a stateless translation layer between AI agents and the SignNow API. It adds zero noise and carries only the signal the agent needs. Every design decision reinforces this identity.
| Principle | Rule |
|---|---|
| Thin Translator | Zero state, zero caching, zero business logic that belongs in the agent. |
| Stateless | No in-memory state between requests, no session objects, no module-level mutable state, no singletons. |
| Tool Minimization | Fewer tools with broader capability. One tool decides internally whether to call document vs. document-group API. |
| Token Efficiency | Every field in a response costs money and context. Carry only the minimum data the agent needs for its next decision. Omit nulls, empty lists, metadata. |
| Testability | Every business logic function is unit-testable by injecting a mocked SignNowAPIClient. If a design makes testing harder, the design is wrong. |
| Specific Errors | Every error message names the operation, entity (with IDs), and cause. |
| YAGNI | Don't add functionality until it's actually needed. No future-proofing for hypothetical requirements. |
| No Infrastructure Coupling | No AWS/GCP/Azure assumptions. |
Agent Request
→ Transport (Starlette / STDIO / SSE)
→ Tool Orchestrator (tools/signnow.py)
→ Token Resolution (TokenProvider)
→ Business Logic (tools/<feature>.py)
→ API Client (signnow_client/client_*.py)
→ SignNow API
→ Response Model (tools/models.py)
→ Agent| # | Layer | Location | May Import | Must NOT Import |
|---|---|---|---|---|
| 1 | API Models | signnow_client/models/ | pydantic only | sn_mcp_server.*, signnow_client/client*.py |
| 2 | API Client | signnow_client/client_*.py | Layer 1, signnow_client/exceptions.py, httpx (via base class) | sn_mcp_server.* (NO upward imports) |
| 3 | Tool Response Models | sn_mcp_server/tools/models.py | pydantic, Layer 1 types (for references) | Raw API passthrough |
| 4 | Tool Business Logic | sn_mcp_server/tools/<feature>.py | Layer 2 (client), Layer 3 (models) | Starlette, other tool modules, token resolution |
| 5 | Tool Orchestrator | sn_mcp_server/tools/signnow.py | Layer 4, TokenProvider | Business logic in orchestrator body |
| 6 | Auth | sn_mcp_server/auth.py, token_provider.py | signnow_client, config | Tools layer |
| 7 | Transport | sn_mcp_server/app.py, cli.py | server.py, config.py, auth.py | Tools directly, API client directly |
Dependency arrow is strictly downward: sn_mcp_server → signnow_client. Never the reverse. Any import from sn_mcp_server inside signnow_client is an immediate reject.
Token resolution belongs in the orchestrator (tools/signnow.py) via _get_token_and_client(). Business logic functions receive token: str as a parameter — they never resolve tokens themselves.
Every API response goes through tools/models.py. Never return raw SignNow JSON. Each field must justify: "Does the agent need this for its next decision?" If no — omit.
# ✅ Specific — operation + entity + cause
raise ValueError(f"Cannot send invite for document '{document_id}': no signers configured")
# ❌ Vague — no context
raise ValueError("Something went wrong")Tokens, passwords, API keys, PEM content must NEVER appear in logs, errors, or tool responses. Use _mask_secret_value() for diagnostic output.
Every tool must be registered in tools/__init__.py → register_tools() and documented in README.md.
auth.py triggers config load → RSA keygen → stdout print → client creation. In tests, mock or avoid importing directly.OAUTH_RSA_PRIVATE_KEY PEM is missing — invalidates all JWTs.OAUTH_ISSUER="" → http://localhost:8000.redirect_target when redirect_uri is absent.Do NOT modify without explicit instruction: pyproject.toml, pytest.ini, AGENTS.md.
AGENTS.md — project governance constitutionARCHITECTURE.md — authoritative architectural specification~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.