Skip to content

Safety model

finos-mcp servers are catalogues. They answer questions about published FINOS standards and validate documents an agent hands them. Nothing they do has a side effect outside the process.

What is enforced, and where

Guarantee Mechanism Code
No tool can write anywhere Tools are registered only through register_tool, which stamps readOnlyHint: true, destructiveHint: false, openWorldHint: false. A gate in the middleware refuses every tools/* request if any registered tool lacks those annotations. There is no configuration flag that relaxes this. core/src/finos_mcp/core/server.py
No network at runtime All framework content and schemas are vendored at build time. Each _vendor/ directory carries SOURCE.json with the upstream repository, ref, commit hash, fetch time and per-file sha256. verify() runs when the server loads its data and in CI; any mismatch aborts. core/src/finos_mcp/core/vendor.py
Bounded input Serialised tool arguments above the policy cap (64 KiB by default, 1 MiB for CDM validation) are rejected before the tool runs, as a structured input_too_large result. middleware, policy.py
Bounded output Results above the output cap (512 KiB) are replaced by a structured output_too_large result with a hint to narrow the request. middleware
Rate limits Token bucket per client and per tool. Exhaustion is a JSON-RPC error (-32029, rate_limited, with retry_after_s) so the host sees it, rather than a tool result that could mislead the model. Buckets are LRU-bounded (10k keys) so session churn cannot exhaust memory. ratelimit.py
Structured errors Anticipated failures are ToolErrors whose text is a JSON ErrorEnvelope (code, message, hint, candidates). Unexpected exceptions are left to the MCP SDK, which logs the traceback server-side and shows the model only Error executing tool <name>. errors.py
Audit log One JSON line per request: timestamp, server, method, tool, argument size and sha256 (raw arguments only with FINOS_MCP_AUDIT_RAW_ARGS=1), outcome, duration, result size. Goes to stderr or FINOS_MCP_AUDIT_PATH. Never stdout, which is the stdio protocol channel. audit.py
Policy only tightens FINOS_MCP_MAX_INPUT_BYTES, FINOS_MCP_MAX_OUTPUT_BYTES, FINOS_MCP_RATE_CALLS, FINOS_MCP_RATE_WINDOW_S, FINOS_MCP_RATE_BURST are applied only when stricter than the server's own defaults. policy.py

Threats considered

  • Prompt injection through served content. The servers return verbatim FINOS text and schemas. A document that contained instructions would be served as data; the servers never act on content. Hosts should treat resource text as untrusted input, as with any retrieval tool.
  • Malicious or oversized inputs. Size caps and Pydantic validation of every argument run before any tool logic. JSON Schema validation of user-supplied documents is bounded by the input cap and by an issue limit.
  • Denial of service. Per-tool rate limits and bounded bucket storage. The heaviest tool, CDM object validation, has a lower limit and its own input cap.
  • Supply chain of vendored content. Hash-pinned manifests; a weekly workflow re-syncs upstream into a scratch directory and opens a reviewable pull request, so content never changes silently.
  • Information leakage in errors. Tracebacks never reach clients; audit logs hash arguments by default.

What is not covered

  • Authentication and transport security for HTTP deployments are the host's responsibility; the MCP SDK's auth hooks can be layered in front of the server.
  • The servers do not sandbox the process; they assume a normal Python runtime.