@oneentry/mcp-platform-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +189 -0
  2. package/data/catalog.json +1 -0
  3. package/data/knowledge.json +1 -0
  4. package/dist/api/audit.d.ts +32 -0
  5. package/dist/api/audit.d.ts.map +1 -0
  6. package/dist/api/audit.js +35 -0
  7. package/dist/api/audit.js.map +1 -0
  8. package/dist/api/auth.d.ts +53 -0
  9. package/dist/api/auth.d.ts.map +1 -0
  10. package/dist/api/auth.js +126 -0
  11. package/dist/api/auth.js.map +1 -0
  12. package/dist/api/catalog.d.ts +39 -0
  13. package/dist/api/catalog.d.ts.map +1 -0
  14. package/dist/api/catalog.js +108 -0
  15. package/dist/api/catalog.js.map +1 -0
  16. package/dist/api/client.d.ts +60 -0
  17. package/dist/api/client.d.ts.map +1 -0
  18. package/dist/api/client.js +196 -0
  19. package/dist/api/client.js.map +1 -0
  20. package/dist/api/policy.d.ts +52 -0
  21. package/dist/api/policy.d.ts.map +1 -0
  22. package/dist/api/policy.js +0 -0
  23. package/dist/api/policy.js.map +1 -0
  24. package/dist/api/shape.d.ts +19 -0
  25. package/dist/api/shape.d.ts.map +1 -0
  26. package/dist/api/shape.js +151 -0
  27. package/dist/api/shape.js.map +1 -0
  28. package/dist/api/types.d.ts +85 -0
  29. package/dist/api/types.d.ts.map +1 -0
  30. package/dist/api/types.js +2 -0
  31. package/dist/api/types.js.map +1 -0
  32. package/dist/bin/cli.d.ts +3 -0
  33. package/dist/bin/cli.d.ts.map +1 -0
  34. package/dist/bin/cli.js +48 -0
  35. package/dist/bin/cli.js.map +1 -0
  36. package/dist/config/config.d.ts +108 -0
  37. package/dist/config/config.d.ts.map +1 -0
  38. package/dist/config/config.js +167 -0
  39. package/dist/config/config.js.map +1 -0
  40. package/dist/index.d.ts +23 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +18 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/knowledge/chunk.d.ts +35 -0
  45. package/dist/knowledge/chunk.d.ts.map +1 -0
  46. package/dist/knowledge/chunk.js +87 -0
  47. package/dist/knowledge/chunk.js.map +1 -0
  48. package/dist/knowledge/loader.d.ts +27 -0
  49. package/dist/knowledge/loader.d.ts.map +1 -0
  50. package/dist/knowledge/loader.js +125 -0
  51. package/dist/knowledge/loader.js.map +1 -0
  52. package/dist/knowledge/search.d.ts +34 -0
  53. package/dist/knowledge/search.d.ts.map +1 -0
  54. package/dist/knowledge/search.js +95 -0
  55. package/dist/knowledge/search.js.map +1 -0
  56. package/dist/knowledge/types.d.ts +47 -0
  57. package/dist/knowledge/types.d.ts.map +1 -0
  58. package/dist/knowledge/types.js +2 -0
  59. package/dist/knowledge/types.js.map +1 -0
  60. package/dist/server.d.ts +16 -0
  61. package/dist/server.d.ts.map +1 -0
  62. package/dist/server.js +48 -0
  63. package/dist/server.js.map +1 -0
  64. package/dist/session.d.ts +43 -0
  65. package/dist/session.d.ts.map +1 -0
  66. package/dist/session.js +75 -0
  67. package/dist/session.js.map +1 -0
  68. package/dist/tools/api-call.d.ts +5 -0
  69. package/dist/tools/api-call.d.ts.map +1 -0
  70. package/dist/tools/api-call.js +169 -0
  71. package/dist/tools/api-call.js.map +1 -0
  72. package/dist/tools/api-discovery.d.ts +5 -0
  73. package/dist/tools/api-discovery.d.ts.map +1 -0
  74. package/dist/tools/api-discovery.js +80 -0
  75. package/dist/tools/api-discovery.js.map +1 -0
  76. package/dist/tools/docs.d.ts +5 -0
  77. package/dist/tools/docs.d.ts.map +1 -0
  78. package/dist/tools/docs.js +113 -0
  79. package/dist/tools/docs.js.map +1 -0
  80. package/dist/tools/guide.d.ts +11 -0
  81. package/dist/tools/guide.d.ts.map +1 -0
  82. package/dist/tools/guide.js +67 -0
  83. package/dist/tools/guide.js.map +1 -0
  84. package/dist/tools/result.d.ts +19 -0
  85. package/dist/tools/result.d.ts.map +1 -0
  86. package/dist/tools/result.js +19 -0
  87. package/dist/tools/result.js.map +1 -0
  88. package/dist/tools/whoami.d.ts +5 -0
  89. package/dist/tools/whoami.d.ts.map +1 -0
  90. package/dist/tools/whoami.js +42 -0
  91. package/dist/tools/whoami.js.map +1 -0
  92. package/dist/transports/http.d.ts +10 -0
  93. package/dist/transports/http.d.ts.map +1 -0
  94. package/dist/transports/http.js +126 -0
  95. package/dist/transports/http.js.map +1 -0
  96. package/dist/transports/stdio.d.ts +8 -0
  97. package/dist/transports/stdio.d.ts.map +1 -0
  98. package/dist/transports/stdio.js +15 -0
  99. package/dist/transports/stdio.js.map +1 -0
  100. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # @oneentry/mcp-platform-server
2
+
3
+ MCP server that lets an AI agent **operate the OneEntry Admin API** — grounded in the project's own
4
+ rules rather than in guesswork.
5
+
6
+ Two things are bundled:
7
+
8
+ - **Knowledge.** ~1 MB of reference docs from `backend-app/.claude/**` and `frontend-app/.claude/**`,
9
+ split into ~1700 searchable sections, plus a hand-written `operating-rules.md` distilling the
10
+ payload rules that actually break calls.
11
+ - **Actuation.** All **478 Admin API operations** as a searchable catalog with parameter and body
12
+ schemas, the permission each one requires, and a risk classification — reachable through one
13
+ generic invoke tool instead of 478 separate ones.
14
+
15
+ Only the Admin API is exposed.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm i -g @oneentry/mcp-platform-server # or use npx, below
21
+ ```
22
+
23
+ ## Local mode (agent and CMS on the same machine)
24
+
25
+ Add to `.mcp.json` in the repo you work from:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "oneentry-mcp-platform": {
31
+ "command": "npx",
32
+ "args": ["-y", "@oneentry/mcp-platform-server", "--back", "../backend-app", "--front", "../frontend-app"],
33
+ "env": {
34
+ "ONEENTRY_CMS_BASE_URL": "http://localhost:3003/api/admin",
35
+ "ONEENTRY_CMS_LOGIN": "your-admin-login",
36
+ "ONEENTRY_CMS_PASSWORD": "your-admin-password"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ `--back`/`--front` are optional: with them the knowledge is read live from the checkouts, so edits
44
+ to `.claude/docs` take effect immediately; without them the bundled snapshot is used.
45
+
46
+ backend-app must be running with `API_TYPE=/api/admin` (default port 3003).
47
+
48
+ ## Remote mode (hosted, many agents, one stand)
49
+
50
+ ```bash
51
+ oneentry-mcp-platform --http --port 8931 --audit /var/log/oneentry-mcp-platform-audit.jsonl \
52
+ --base-url https://stand.example/api/admin \
53
+ --allowed-origins https://agent.example
54
+ ```
55
+
56
+ Each MCP session carries its own CMS identity, passed as connection headers — never as tool
57
+ arguments, so a prompt-injected instruction cannot swap identities:
58
+
59
+ | header | meaning |
60
+ |---|---|
61
+ | `x-cms-token` | pre-issued Admin API access token (preferred) |
62
+ | `x-cms-login` + `x-cms-password` | credentials to log in with |
63
+
64
+ Sessions never share tokens or confirm tokens. `GET /health` reports liveness and session count.
65
+
66
+ ## Docker
67
+
68
+ ```bash
69
+ cp .env.example .env # fill in ONEENTRY_CMS_LOGIN / ONEENTRY_CMS_PASSWORD
70
+ docker compose up --build mcp # remote mode on :8931
71
+ curl -s http://127.0.0.1:8931/health # {"ok":true,"mode":"remote","sessions":0}
72
+ ```
73
+
74
+ | service | profile | mode |
75
+ |---|---|---|
76
+ | `mcp` | — | remote (Streamable HTTP) on `:8931` |
77
+ | `mcp-live` | `live` | remote, knowledge read live from the mounted checkouts |
78
+ | `stdio` | `stdio` | local stdio — usable directly as an MCP client command |
79
+
80
+ ```bash
81
+ docker compose --profile live up --build mcp-live
82
+ docker compose --profile stdio run --rm -T stdio
83
+ ```
84
+
85
+ A stand running on the host is reachable as `host.docker.internal` (wired via `host-gateway`), so
86
+ the default base URL inside the container is `http://host.docker.internal:3003/api/admin`. The audit
87
+ log is bind-mounted to `./.audit`. Details and gotchas: [.claude/docs/docker.md](.claude/docs/docker.md).
88
+
89
+ To point an MCP client at the containerised stdio server:
90
+
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "oneentry-mcp-platform": {
95
+ "command": "docker",
96
+ "args": ["compose", "-f", "/abs/path/to/docker-compose.yml", "--profile", "stdio",
97
+ "run", "--rm", "-T", "stdio"]
98
+ }
99
+ }
100
+ }
101
+ ```
102
+
103
+ ## The seven tools
104
+
105
+ | tool | what it does |
106
+ |---|---|
107
+ | `cms_guide` | read first: mode, policy, API map, the order to use the other tools |
108
+ | `cms_docs_search` | search the knowledge base, returns sections with snippets |
109
+ | `cms_docs_read` | read one section, with its sibling sections listed for paging |
110
+ | `cms_api_search` | find operations by keyword / tag / method / mutating |
111
+ | `cms_api_describe` | params, body schema, required permission, risk, linked docs |
112
+ | `cms_api_call` | execute one operation; `dryRun` and confirm-gating for mutations |
113
+ | `cms_whoami` | mode, base URL, admin + permissions, catalog/knowledge versions and warnings |
114
+
115
+ Plus two MCP resources: `oneentry://knowledge/mcp/operating-rules` and `oneentry://knowledge/index`.
116
+
117
+ ## Write safety
118
+
119
+ Read-only by default. `--allow` (or `ONEENTRY_MCP_ALLOW`) raises it:
120
+
121
+ | level | GET | POST/PUT/PATCH | DELETE |
122
+ |---|---|---|---|
123
+ | `read` (default) | ✅ | ❌ | ❌ |
124
+ | `write` | ✅ | ✅ | ❌ |
125
+ | `destructive` | ✅ | ✅ | ✅ confirm-gated |
126
+
127
+ - A refusal by level happens **before authentication** — no HTTP request is made at all.
128
+ - Deletes, and every path in the permanently gated list (`immutable-settings`, `admins`, `backups`,
129
+ `modules`, `payments/webhook`, `settings-general`, `system/captcha-keys`,
130
+ `auth/logout/all-users`), require a **confirm token**: the first call returns the token *and the
131
+ current state of the target*, fetched via the sibling GET, so a human can see what is about to
132
+ change. The token is bound to `hash(opId + arguments)`, single-use, valid 5 minutes.
133
+ - Permissions are checked locally first: if the admin lacks the `AdminPermissionsEnum` value the
134
+ operation declares, the call is refused without being sent.
135
+ - Every non-GET call is appended to a JSONL audit log (arguments are hashed, not stored). Required
136
+ in remote mode.
137
+
138
+ ## Keeping the bundled data in sync
139
+
140
+ `data/catalog.json` and `data/knowledge.json` are committed build outputs:
141
+
142
+ ```bash
143
+ npm run sync:from-cms -- --back ../backend-app --front ../frontend-app
144
+ npm run check:drift # CI: fails if the committed data is stale
145
+ ```
146
+
147
+ Both commands fall back to sibling checkouts (`../dnk-back`, `../dnk-front`) when the flags are
148
+ omitted, so the bare form works in the usual layout. `check:drift` runs automatically as
149
+ `prestart`, i.e. before `npm start` — it compares content only, and **skips** (exit 0) when no
150
+ checkout is next to the repo, so a deployment without the CMS sources still starts. It does not
151
+ run before `npm run dev`: the ts-morph pass over every controller takes ~20 s, which is too slow
152
+ for a restart loop.
153
+
154
+ Refreshing `swagger.json` itself is dnk-back's job — boot it once with `API_TYPE=/api/admin` and it
155
+ rewrites `local-static/swagger.json` on startup.
156
+
157
+ The catalog is built from two sources, because neither alone is enough:
158
+
159
+ 1. `backend-app/local-static/swagger.json` — paths, operation ids and schemas.
160
+ 2. `backend-app/src/**/*.controller.ts` via ts-morph — the `@GrantByPermission` decorators, which
161
+ never reach swagger. Base classes are walked, since Nest derives the operation id from the
162
+ registered controller while the handler often lives in a parent.
163
+
164
+ Two classes of inconsistency in dnk-back are surfaced as catalog `warnings` (visible in
165
+ `cms_whoami`) rather than silently swallowed:
166
+
167
+ - handlers that declare a permission but are missing from `swagger.json` — a stale swagger snapshot;
168
+ - permissions required by controllers but absent from `AdminPermissionsEnum` (`users.export`,
169
+ `orders.export`, `payments.export`) — no admin can hold these, so those endpoints always 403.
170
+
171
+ ### The swagger is not valid JSON Schema
172
+
173
+ 311 `type` values in the source swagger are TypeScript expressions (`Record<string, any>`,
174
+ `CommonLocalizeInfos`, `AttributesSets`, `image`, `date`). The build normalizes what it can and
175
+ marks the rest `"x-loose": true` with the original under `x-source-type`. For loose fields the
176
+ `example` is the contract, and client-side body validation is deliberately advisory — the server
177
+ is the real validator, so a call is never blocked because a loose field could not be checked.
178
+
179
+ ## Tests
180
+
181
+ ```bash
182
+ npm test # unit + MCP protocol + remote-mode transport (no stand needed)
183
+ npm run test:integration # against a live dnk-back; skips cleanly if unreachable
184
+ ```
185
+
186
+ The integration suite needs backend-app on `:3003` with `API_TYPE=/api/admin` plus
187
+ `ONEENTRY_CMS_LOGIN`/`ONEENTRY_CMS_PASSWORD`. It walks search → describe → call, asserts a read-only refusal
188
+ sends nothing, and runs a full create → confirm-gated delete cycle on a throwaway template while
189
+ checking the audit log.