Skip to content

feat: read-only MCP server generated from the OpenAPI document - #47

Merged
Robertzu43 merged 10 commits into
mainfrom
feat/mcp-server
Aug 26, 2026
Merged

feat: read-only MCP server generated from the OpenAPI document#47
Robertzu43 merged 10 commits into
mainfrom
feat/mcp-server

Conversation

@Robertzu43

Copy link
Copy Markdown
Collaborator

Summary

  • packages/mcp/easydocs_mcp.py — a read-only MCP server for Claude Code, Cursor, Codex, Gemini CLI and Claude Desktop. One PEP 723 script: it fetches the install's own /openapi/v1.json and, via FastMCP's from_openapi, exposes 17 GET endpoints as named tools (list_documents, list_versions, compare_versions, list_audit_events, list_my_approvals, …). An ordered route_maps allowlist with a deny-all catch-all means every write, auth, WOPI, WebDAV, share and SSE route is excluded, and a new endpoint is excluded by default.
  • Runs on the user's machine with their own ed_ token, so DocumentAuthorization applies untouched. Zero changes to src/. Read-only is a property of the tool set, not the token — the README says so plainly.
  • packages/* now exists and is MIT, as README/CONTRIBUTING/ADR-13 always said it would be; their "does not exist yet" wording is flipped. The .NET template's NuGet packages/ ignore rule is negated for /packages/**.
  • One pytest file pins the tool surface against the committed OpenAPI snapshot: exact 17-name set, and every tool's route is GET. Runs in the existing docs-build CI job.

Design: docs/superpowers/specs/2026-08-26-mcp-server-design.md · Plan: docs/superpowers/plans/2026-08-26-mcp-server.md. Follow-ups deliberately out of scope (a versions/{vid}/text endpoint, opt-in write tools, a hosted /mcp with OAuth) are listed in the spec.

Test plan

  • uv run --with "fastmcp>=3.4,<4" --with pytest pytest packages/mcp -q → 2 passed
  • mkdocs build --strict → exit 0
  • Live run against a local stack through a real FastMCP stdio client: 17 tools listed; whoami and list_documents return API JSON; a missing document surfaces the API's problem+json detail as a tool error
  • Missing env vars → KeyError: 'EASYDOCS_URL', exit 1
  • CI green (docs-build now runs the package test)

🤖 Generated with Claude Code

Robertzu43 and others added 10 commits August 26, 2026 14:55
…cument

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
…I verification

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Rename in the adapter instead of touching src/ (fork kept if FastMCP needs
operationId); single PEP 723 script instead of pyproject; one Python test
with a method assertion instead of a C# guard; reuse the docs-build job;
README as the one doc; client timeout and URL normalisation made explicit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Also un-ignore /packages/** — the .NET template's NuGet rule was swallowing it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
… server

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
…nges

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Robertzu43 <robertzu43@icloud.com>
Copilot AI lite review requested due to automatic review settings August 26, 2026 20:28

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 a locally-run, read-only Model Context Protocol (MCP) server under packages/mcp/ that generates tools from the instance’s /openapi/v1.json, along with documentation/licensing updates and CI coverage to pin the exposed tool surface.

Changes:

  • Introduces packages/mcp/easydocs_mcp.py (PEP 723 script) and a pytest file that asserts the exact tool set and enforces GET-only tools.
  • Adds end-user setup docs and MIT licensing for packages/*, and updates repo docs to reflect the new packages/ boundary.
  • Extends docs-build CI to run the MCP package tests and updates .gitignore to keep /packages/** tracked.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Adds MCP mention in API section; clarifies AGPL vs MIT boundary for packages/*.
packages/mcp/easydocs_mcp.py New FastMCP server script generated from OpenAPI with allowlisting and tool renaming.
packages/mcp/test_easydocs_mcp.py Pins tool surface to the committed OpenAPI snapshot (exact names + GET-only).
packages/mcp/README.md Installation/config instructions for multiple MCP clients and token handling guidance.
packages/mcp/LICENSE MIT license for the MCP package.
docs/superpowers/specs/2026-08-26-mcp-server-design.md Design spec documenting the approach and constraints.
docs/superpowers/plans/2026-08-26-mcp-server.md Implementation plan describing steps, tests, and CI integration.
docs/architecture-decisions.md Updates ADR-13 to reflect packages/* as the MIT client boundary.
docs-site/docs/automation-recipes.md Links to MCP server as an alternative to raw curl usage.
CONTRIBUTING.md Updates licensing guidance to reflect packages/* MIT boundary.
CHANGELOG.md Adds an unreleased entry for MCP support and packages licensing.
.gitignore Un-ignores repo-root /packages/** while keeping NuGet restore ignores.
.github/workflows/ci.yml Runs MCP pytest in docs-build.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@@ -0,0 +1,91 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["fastmcp>=3.4,<4"]
Comment thread .github/workflows/ci.yml
Comment on lines 177 to +186
- name: Install MkDocs Material
run: pip install mkdocs-material mkdocs-swagger-ui-tag

# packages/mcp is a Python client of the API, and this job already has Python. Its test pins
# the read-only tool surface against the committed OpenAPI snapshot (which the .NET job pins
# against the served document), so a write endpoint cannot leak into the MCP tool set unnoticed.
- name: Test the MCP server package
run: |
pip install "fastmcp>=3.4,<4" pytest
pytest packages/mcp -q
Comment on lines +25 to +28
def _tools():
spec = json.loads(SNAPSHOT.read_text())
server = easydocs_mcp.build(spec, httpx.AsyncClient(base_url="http://example.invalid"))
return asyncio.run(server.list_tools())
Comment on lines +43 to +55
READ_TAGS = ["Documents", "Folders", "Audit", "Members", "Approvals", "Publishing", "Merging", "Copies"]

# Evaluated in order, first match wins. The trailing catch-all is the point: a new endpoint is
# excluded until someone adds it here on purpose.
ROUTE_MAPS = [
# Binary docx/pdf — useless to a model and large. Carries the Documents tag, so it must go first.
RouteMap(methods=["GET"], pattern=r"^/api/v1/versions/\{vid\}/download$", mcp_type=MCPType.EXCLUDE),
*[RouteMap(methods=["GET"], tags={t}, mcp_type=MCPType.TOOL) for t in READ_TAGS],
RouteMap(methods=["GET"], pattern=r"^/api/v1/(me|org|orgs)$", mcp_type=MCPType.TOOL),
# Everything else: all writes, Auth/MFA/SSO/Tokens, Sharing, Editing, the SSE stream, WOPI,
# WebDAV, /s/, /health, and GET /api/v1/org/members.
RouteMap(pattern=r".*", mcp_type=MCPType.EXCLUDE),
]
@Robertzu43
Robertzu43 merged commit e5c80b7 into main Aug 26, 2026
7 checks passed
@Robertzu43
Robertzu43 deleted the feat/mcp-server branch August 26, 2026 20:59
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.

2 participants