regulated-reporting-mcp

regulated-reporting-mcp

MCP server for regulated financial reporting on Workiva, enabling agents to search, read, and write to Workiva workbooks with policy-gated mutations, readback verification, and immutable receipts. Supports both a compact 3-tool facade and a full 117-tool catalog, plus a credential-free mock mode.

Category
访问服务器

README

regulated-reporting-mcp

CI

Provenance. Sanitized public extract published August 2026. Public git history is publication history, not the original private development timeline. The City of Riverton demo and all organizations are fictional. No client data or credentials appear in this repository. Private client history remains confidential; public claims are limited to inspectable artifacts.

I built this MCP server around a problem I ran into while working on government financial reports in Workiva: a successful API response does not necessarily mean a change was applied, and an applied change has not necessarily been checked. The server keeps those outcomes separate, requires confirmation before writes, and records what happened without putting secrets or cell payloads in the receipt.

You can run the full path without Workiva credentials. The included mock covers the confirmation gate, rate-limit retry, asynchronous operation polling, readback, and receipt creation in under a minute.

Mock-mode demo: list, read, gated write, verified readback, receipt

$ pip install -e . && workiva-mcp-demo

=== 5. Attempt the write WITHOUT confirm_write (contract blocks it) ===
{"error": "Tool requires confirmation: workiva_write_verified",
 "requires_confirmation": true, "risk": "write", ...}

=== 6. Re-issue with confirm_write=true (verified write + readback) ===
{"status": "verified", "written_cells": 3, "mismatches": [],
 "state": "verified", "receipt_uri": "workiva-receipt://2026...-workiva-write-verified-..."}

The problem it solves

Financial statements are unforgiving. A wrong number in a statement workbook is not a harmless UI bug. The default server therefore puts every mutation behind a reviewed tool contract and writes a redacted, create-once receipt. A tool that promises readback must report either verified or verification_failed. If it cannot tell, the dispatcher returns indeterminate.

What is implemented

  1. OAuth2 client credentials. The standard-library implementation caches a token, refreshes it before expiry, and clears and retries once after a 401 (auth.py, http_client.py).
  2. The less tidy parts of an enterprise API. The client handles 202 polling through both header and body operation locations, @nextLink and next_url pagination, parallel row-range reads, 429 backoff with Retry-After, and binary-safe xlsx/PDF exports (http_client.py, reapi/).
  3. A write gate that is enforced in code. All 117 registered tools have a reviewed effect / confirmation / proof contract. The compact dispatcher refuses tools with no contract, redacts mutation receipts, and treats missing readback as indeterminate (tool_contracts.py, compact_server.py, execution.py, receipt_store.py).

Architecture

flowchart LR
    C[MCP client] --> CS["compact server (3 tools):<br/>search / call / batch"]
    CS --> TC["tool contracts<br/>effect · confirmation · proof<br/>(fail-closed)"]
    TC --> FS["raw implementation registry<br/>(117-tool catalog; not served by default)"]
    FS --> T["tool families:<br/>cells · spreadsheets · documents ·<br/>files · linking · wdata · chains ·<br/>tasks · presentations · admin"]
    T --> H["http client<br/>OAuth2 · 401 retry · 429 backoff · pagination"]
    T -. selected mutation tools .-> EX["execution state machine<br/>202 ≠ applied ≠ verified"]
    EX --> H
    H --> W[(Workiva API)]
    H -. WORKIVA_MCP_MOCK=1 .-> M[(FakeWorkiva<br/>in-memory)]
    CS --> R["payload-redacted, create-once receipts +<br/>result artifacts<br/>(MCP resources)"]

The choices that matter:

  • For tools that use the mutation state machine, a 202 with no operation location is indeterminate, never success. execution.py accepts injected request and polling functions, so this behavior is testable without credentials. Not every raw tool uses that state machine; the test map says which claims apply where.
  • The manifest, not a guess based on the tool name, controls dispatch. scripts/generate_tool_contract_manifest.py --check verifies exact coverage of the 117-tool registry. Name-based classification is only a drift warning.
  • A receipt records an attempt and its reported outcome. It is not readback. proof: readback requires a deterministic verification result; otherwise the state is indeterminate and the receipt says pending_readback.
  • workiva-mcp and python -m workiva_mcp start the guarded three-tool server. Serving the raw catalog requires an explicit unsafe opt-in because direct calls bypass confirmation and receipt handling.
  • The three-tool front end reduces the listed tool count by about 97% and the listing payload by more than 90%, as measured by catalog_benchmark(). Large results become workiva-result://… MCP resources instead of chat payloads.
  • reapi/ is an offline-only layer for typed 4xx/5xx errors, retries, polling, and receipt validation. It performs no I/O and requires no authentication.

Quickstart (no credentials needed)

git clone https://github.com/dbett4/regulated-reporting-mcp
cd regulated-reporting-mcp
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

workiva-mcp-demo        # end-to-end mock demo: list → read → gated write → verified readback
pytest                  # 126 credential-free tests
./scripts/proof.sh      # lint + manifest + tests + full offline demo

The demo uses FakeWorkiva, an in-memory transport with a synthetic "City of Riverton" ACFR-style workbook. It runs the same gate, one-shot 429 retry, 202 polling, readback, and receipt code used by the real transport.

The demo's verified write stores its receipt under ~/.workiva_mcp/receipts/ (the same write-once receipt store used against a real workspace). Set WORKIVA_MCP_RECEIPT_DIR to redirect receipts to any other directory.

Use it from Claude (mock mode)

{
  "mcpServers": {
    "workiva": {
      "command": "workiva-mcp",
      "env": { "WORKIVA_MCP_MOCK": "1" }
    }
  }
}

Against a real Workiva workspace

Provide OAuth2 client credentials (created in Workiva under an org API grant) and drop the mock flag:

export WORKIVA_CLIENT_ID=...
export WORKIVA_CLIENT_SECRET=...
export WORKIVA_REGION=us        # us | eu | apac
workiva-mcp                     # guarded 3-tool compact facade (default)

Credentials can also live in a .env file next to the package or pointed at with WORKIVA_ENV_FILE.

The raw 117-tool FastMCP server remains available for isolated development and for registry discovery inside the compact dispatcher. It bypasses confirmation and receipt handling, so it is deliberately awkward to start:

WORKIVA_MCP_CATALOG_MODE=full \
WORKIVA_MCP_ALLOW_UNGATED_FULL_CATALOG=1 \
workiva-mcp

Do not expose that mode to an autonomous or untrusted caller.

Tests

All 126 tests run without credentials. Transport tests use injected fakes; tests/test_mock_mode.py drives the actual tool stack through FakeWorkiva. Entrypoint tests check that compact mode is the default and that full-catalog mode fails unless both unsafe flags are present. Contract tests cover formula cells and exceptions after dispatch, and make sure a readback promise cannot be reported as applied_unverified.

See how to check each claim and the security notes.

Status

This is my public, sanitized implementation of patterns I used while building and tying out annual comprehensive financial reports in Workiva. It is not a copy of a client repository. It contains no engagement-management code, and all workbook values, identifiers, credentials, and organizations are synthetic.

License

MIT — see LICENSE.


Built by Dave Bettner.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选