nc-forms-mcp
by erniomaldo
README.md
# nc-forms-mcp
**MCP server for the Nextcloud Forms API v3.** Create, update, clone, and validate
forms β without ever leaking an app password into the agent's context.
[](https://python.org)
[](https://github.com/modelcontextprotocol)
[](LICENSE)
---
<p align="center">
<strong>Push Forms API operations into a sealed MCP server.</strong><br>
<em>Your agent calls tools, not curl. The password never leaves the env.</em>
</p>
---
## π€¨ The Problem
Nextcloud Forms has a REST API at `/ocs/v2.php/apps/forms/api/v3/` that uses
**HTTP Basic Auth with an app password**. When an AI agent calls this API directly:
| Issue | What happens | Consequence |
|-------|-------------|-------------|
| Credential leakage | The password passes through the LLM's context | Stored in logs, prompt caches, conversation history |
| Tool sprawl | Every Forms operation is a raw `curl` invocation | Inconsistent error handling, no validation |
| No isolation | The same credential gets reused for WebDAV, shares, etc. | Scope creep β operations outside Forms API |
| Repetition | Auth headers, error parsing, and retry logic in every call | Wasteful tokens and fragile code |
**Bottom line:** credentials in agent context = risk. Raw API calls in conversation = tech debt.
---
## β
The Solution
Wrap the Forms API in an **MCP server** that:
1. Reads credentials from **environment variables only** β the agent never sees them
2. Exposes **16 focused tools** β `forms_get`, `questions_add`, `options_update`, etc.
3. Runs as a **stdio subprocess** managed by the Hermes MCP client
4. Returns structured JSON β no parsing needed
5. Validates form health with a single tool call
```
ββββββββββββββββ MCP stdio ββββββββββββββββββββ HTTPS Basic Auth ββββββββββββββββββββ
β β ββββββββββββββββ β ββββββββββββββββββββββ β
β Hermes β ββββββββββββββββ nc-forms-mcp β ββββββββββββββββββββββ Nextcloud Forms β
β Agent β β (Python process) β β API v3 β
β β β NC_FORMS_PASSWORDβ β β
ββββββββββββββββ β (env only, β ββββββββββββββββββββ
β never in chat) β
ββββββββββββββββββββ
```
---
## π¦ Installation
### Prerequisites
- Python 3.10+
- Nextcloud instance with **Forms** app (v5+)
- Nextcloud **app password** (Settings β Security β Devices & sessions)
### Install
```bash
git clone https://github.com/erniomaldo/nc-forms-mcp.git ~/Proyectos/nc-forms-mcp
cd ~/Proyectos/nc-forms-mcp
# Hermes uses its own venv β install there:
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python -e .
```
Or use your project's venv:
```bash
uv venv && uv pip install -e .
```
---
## π§ Hermes Configuration
Add to `~/.hermes/config.yaml`:
```yaml
mcp_servers:
nc-forms:
command: "uv"
args:
- "run"
- "--directory"
- "/home/ernesto-personal/Proyectos/nc-forms-mcp"
- "nc-forms-mcp"
env:
NC_FORMS_USER: "erniomaldo"
NC_FORMS_PASSWORD: "your-app-password-here"
NC_FORMS_BASE: "https://base.agendasencilla.com"
timeout: 30
```
**Restart Hermes** after adding. On startup it auto-discovers 16 tools prefixed
`mcp_nc_forms_*`.
---
## π οΈ Tools
| Tool | What it does | Use case |
|------|-------------|----------|
| `forms_list` | List all owned forms | Dashboard / overview |
| `forms_get` | Get full form with questions, options, shares | Read current state |
| `forms_create` | Create an empty form | Start a new brief/survey |
| `forms_update` | Update title, description, settings | Iterate on form metadata |
| `forms_clone` | Clone a form (no submissions) | V1 β V2 iteration |
| `forms_delete` | Permanently delete a form | Cleanup |
| `questions_list` | List all questions in a form | Overview |
| `questions_get` | Get a single question by ID | Check specific question |
| `questions_add` | Add a question (multiple choice, text, etc.) | Build form content |
| `questions_update` | Update question text, description, isRequired | Refine wording |
| `questions_delete` | Remove a question | Prune sections |
| `questions_reorder` | Reorder questions by ID array | Reorganize sections |
| `options_add` | Add options to a choice question | Populate answer choices |
| `options_update` | Update a single option's text | Fix typos |
| `options_delete` | Remove an option | Cleanup |
| `forms_validate` | Check all questions have valid optionTypes | QA before publishing |
### Tool naming
MCP prefix: `mcp_nc_forms_` + tool name (underscores replace hyphens).
Example: `mcp_nc_forms_forms_get` β get form details.
---
## π Usage (after Hermes integration)
```python
# List all forms
mcp_nc_forms_forms_list()
# Get form 8 with all questions + options
mcp_nc_forms_forms_get({ "form_id": 8 })
# Add a question to form 8
mcp_nc_forms_questions_add({
"form_id": 8,
"type": "multiple_unique",
"text": "π¨ 1.2 ΒΏColores definidos?"
})
# Add options
mcp_nc_forms_options_add({
"form_id": 8,
"question_id": 74,
"option_texts": ["β
De acuerdo", "π‘ Cerca pero ajustes", "β No se parece"],
"option_type": "choice"
})
# Validate before publishing
mcp_nc_forms_forms_validate({ "form_id": 8 })
# β {"healthy": true, "total_questions": 32, "issues": []}
```
---
## π§ͺ CLI Testing (without Hermes)
```bash
export NC_FORMS_USER="erniomaldo"
export NC_FORMS_PASSWORD="your-app-password"
export NC_FORMS_BASE="https://base.agendasencilla.com"
nc-forms-mcp forms_list '{}'
nc-forms-mcp forms_get '{"form_id": 8}'
nc-forms-mcp forms_validate '{"form_id": 8}'
```
---
## ποΈ Project Structure
```
nc-forms-mcp/
βββ pyproject.toml # Project metadata + dependencies
βββ README.md # This file
βββ LICENSE
βββ nc_forms_mcp/
β βββ __init__.py # Package init (re-exports server)
β βββ server.py # MCP server: auth, route, tools, entry points
βββ tests/ # Coming soon
```
---
## π Security
- **Credentials live in `env` config** β the agent never sees `NC_FORMS_PASSWORD`
- The app password is **scoped to Forms API only** β WebDAV, Shares, Calendar,
Collectives, and other Nextcloud features use their own MCP tools with their
own auth
- Error messages redact credential-like patterns
- The MCP server process inherits a **filtered environment** (Hermes strips
secrets from the subprocess env except what you explicitly set in `env`)
### What NOT to do
```yaml
# β WRONG β password leaks into agent context
mcp_servers:
nc-forms:
command: "python3"
args: ["server.py", "--password", "TEZiJ-..."]
```
```yaml
# β
CORRECT β password stays in env
mcp_servers:
nc-forms:
command: "uv"
args: ["run", "--directory", "~/Proyectos/nc-forms-mcp", "nc-forms-mcp"]
env:
NC_FORMS_PASSWORD: "TEZiJ-..."
```
---
## π‘ Why not just use curl?
| Approach | Secret leaks? | Inconsistent errors? | Token waste? | Reusable? |
|----------|:---:|:---:|:---:|:---:|
| Raw curl in conversation | β
Always | β
Every call | β
Headers + parsing | β No |
| Python script in /tmp | β
In code | β
Per script | β
Duplicated logic | β Per task |
| **MCP server** | β Never | β Unified | β One setup | β
All tasks |
---
## π Comparison with alternative approaches
| | nc-forms-mcp | Forms web UI | Direct REST API (curl) | Custom script |
|---|---|---|---|---|
| **Setup** | 5 lines in config.yaml | Zero (browser) | Zero (curl installed) | Python + requests |
| **Speed** | Instant (stdio) | Point-and-click | Fast | Depends on quality |
| **Repeatability** | β
Always same tools | β Manual clicks | β Varies per call | β
If committed |
| **Validation** | β
`forms_validate` built-in | β Manual review | β Never | β Rarely |
| **CI/CD ready** | β
Yes | β No | β οΈ Needs auth wrapper | β
If committed |
| **Secret safety** | β
In env only | β Browser session | β In command args | β οΈ In code or env |
| **Token cost** | Low (one discovery) | N/A | High (per call) | Low (per script) |
---
## π Related Projects
- **[agentcheckpoint](https://github.com/erniomaldo/agentcheckpoint)** β atomic state store for multi-agent coordination (SQLite + MCP)
- **[Hermes Agent](https://hermes-agent.nousresearch.com/)** β the personal AI agent that runs this MCP server
---
## π License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues