@oneentry/mcp-platform-server 0.1.0 → 0.1.1

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 (71) hide show
  1. package/README.md +113 -71
  2. package/data/permissions.json +411 -0
  3. package/dist/api/build-catalog.d.ts +24 -0
  4. package/dist/api/build-catalog.d.ts.map +1 -0
  5. package/dist/api/build-catalog.js +257 -0
  6. package/dist/api/build-catalog.js.map +1 -0
  7. package/dist/api/catalog.d.ts +22 -3
  8. package/dist/api/catalog.d.ts.map +1 -1
  9. package/dist/api/catalog.js +69 -4
  10. package/dist/api/catalog.js.map +1 -1
  11. package/dist/api/normalize-schema.d.ts +14 -0
  12. package/dist/api/normalize-schema.d.ts.map +1 -0
  13. package/dist/api/normalize-schema.js +151 -0
  14. package/dist/api/normalize-schema.js.map +1 -0
  15. package/dist/api/swagger-source.d.ts +22 -0
  16. package/dist/api/swagger-source.d.ts.map +1 -0
  17. package/dist/api/swagger-source.js +76 -0
  18. package/dist/api/swagger-source.js.map +1 -0
  19. package/dist/api/types.d.ts +1 -1
  20. package/dist/api/types.d.ts.map +1 -1
  21. package/dist/bin/cli.js +11 -3
  22. package/dist/bin/cli.js.map +1 -1
  23. package/dist/config/config.d.ts +84 -10
  24. package/dist/config/config.d.ts.map +1 -1
  25. package/dist/config/config.js +69 -6
  26. package/dist/config/config.js.map +1 -1
  27. package/dist/index.d.ts +9 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +5 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/knowledge/github.d.ts +46 -0
  32. package/dist/knowledge/github.d.ts.map +1 -0
  33. package/dist/knowledge/github.js +214 -0
  34. package/dist/knowledge/github.js.map +1 -0
  35. package/dist/knowledge/loader.d.ts +46 -19
  36. package/dist/knowledge/loader.d.ts.map +1 -1
  37. package/dist/knowledge/loader.js +92 -105
  38. package/dist/knowledge/loader.js.map +1 -1
  39. package/dist/knowledge/search.d.ts +11 -0
  40. package/dist/knowledge/search.d.ts.map +1 -1
  41. package/dist/knowledge/search.js +36 -3
  42. package/dist/knowledge/search.js.map +1 -1
  43. package/dist/knowledge/tar.d.ts +22 -0
  44. package/dist/knowledge/tar.d.ts.map +1 -0
  45. package/dist/knowledge/tar.js +120 -0
  46. package/dist/knowledge/tar.js.map +1 -0
  47. package/dist/knowledge/types.d.ts +31 -10
  48. package/dist/knowledge/types.d.ts.map +1 -1
  49. package/dist/server.d.ts +3 -2
  50. package/dist/server.d.ts.map +1 -1
  51. package/dist/server.js +13 -6
  52. package/dist/server.js.map +1 -1
  53. package/dist/session.d.ts +6 -0
  54. package/dist/session.d.ts.map +1 -1
  55. package/dist/session.js +8 -0
  56. package/dist/session.js.map +1 -1
  57. package/dist/tools/guide.d.ts.map +1 -1
  58. package/dist/tools/guide.js +9 -2
  59. package/dist/tools/guide.js.map +1 -1
  60. package/dist/tools/whoami.d.ts.map +1 -1
  61. package/dist/tools/whoami.js +4 -0
  62. package/dist/tools/whoami.js.map +1 -1
  63. package/dist/transports/http.d.ts.map +1 -1
  64. package/dist/transports/http.js +26 -1
  65. package/dist/transports/http.js.map +1 -1
  66. package/dist/transports/stdio.js +1 -1
  67. package/dist/transports/stdio.js.map +1 -1
  68. package/knowledge/operating-rules.md +176 -0
  69. package/package.json +4 -4
  70. package/data/catalog.json +0 -1
  71. package/data/knowledge.json +0 -1
package/README.md CHANGED
@@ -3,14 +3,20 @@
3
3
  MCP server that lets an AI agent **operate the OneEntry Admin API** — grounded in the project's own
4
4
  rules rather than in guesswork.
5
5
 
6
- Two things are bundled:
6
+ Nothing is frozen into the package. Both halves are fetched at runtime and cached:
7
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.
8
+ - **Knowledge.** Small markdown files from a public GitHub repository
9
+ ([ONEENTRY-PLATFORM/oneentry-platform-rules](https://github.com/ONEENTRY-PLATFORM/oneentry-platform-rules)),
10
+ chunked into searchable sections. Editing a doc there — through the GitHub UI, a PR, whatever — takes effect on the next
11
+ restart of any server, with no npm release.
12
+ - **Actuation.** Every Admin API operation, built from the stand's own
13
+ `GET /api/admin/workflows/nodes/admin-api/swagger.json`, with parameter and body schemas, the
14
+ permission each one requires, and a risk classification — reachable through one generic invoke
15
+ tool instead of one tool per endpoint. The catalog always describes the API that is actually
16
+ running, not a snapshot of it.
17
+
18
+ The one thing still committed here is `data/permissions.json`: `@GrantByPermission` is a decorator
19
+ and never reaches any swagger, so that map has to be recovered from the CMS sources.
14
20
 
15
21
  Only the Admin API is exposed.
16
22
 
@@ -29,7 +35,7 @@ Add to `.mcp.json` in the repo you work from:
29
35
  "mcpServers": {
30
36
  "oneentry-mcp-platform": {
31
37
  "command": "npx",
32
- "args": ["-y", "@oneentry/mcp-platform-server", "--back", "../backend-app", "--front", "../frontend-app"],
38
+ "args": ["-y", "@oneentry/mcp-platform-server"],
33
39
  "env": {
34
40
  "ONEENTRY_CMS_BASE_URL": "http://localhost:3003/api/admin",
35
41
  "ONEENTRY_CMS_LOGIN": "your-admin-login",
@@ -40,8 +46,9 @@ Add to `.mcp.json` in the repo you work from:
40
46
  }
41
47
  ```
42
48
 
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.
49
+ No checkout of the CMS is needed. On first run the knowledge repository is downloaded once and
50
+ cached; later runs re-use the cache and only ask GitHub whether the commit moved, at most once an
51
+ hour.
45
52
 
46
53
  backend-app must be running with `API_TYPE=/api/admin` (default port 3003).
47
54
 
@@ -63,43 +70,6 @@ arguments, so a prompt-injected instruction cannot swap identities:
63
70
 
64
71
  Sessions never share tokens or confirm tokens. `GET /health` reports liveness and session count.
65
72
 
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
73
  ## The seven tools
104
74
 
105
75
  | tool | what it does |
@@ -110,7 +80,7 @@ To point an MCP client at the containerised stdio server:
110
80
  | `cms_api_search` | find operations by keyword / tag / method / mutating |
111
81
  | `cms_api_describe` | params, body schema, required permission, risk, linked docs |
112
82
  | `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 |
83
+ | `cms_whoami` | mode, base URL, admin + permissions, knowledge commit, catalog state and warnings |
114
84
 
115
85
  Plus two MCP resources: `oneentry://knowledge/mcp/operating-rules` and `oneentry://knowledge/index`.
116
86
 
@@ -135,42 +105,114 @@ Read-only by default. `--allow` (or `ONEENTRY_MCP_ALLOW`) raises it:
135
105
  - Every non-GET call is appended to a JSONL audit log (arguments are hashed, not stored). Required
136
106
  in remote mode.
137
107
 
138
- ## Keeping the bundled data in sync
108
+ ## Where the knowledge comes from
139
109
 
140
- `data/catalog.json` and `data/knowledge.json` are committed build outputs:
110
+ A public GitHub repository of small markdown files. Layout:
111
+
112
+ ```
113
+ knowledge/
114
+ mcp/operating-rules.md -> docId "mcp/operating-rules"
115
+ back/docs/menus-module.md -> docId "back/docs/menus-module"
116
+ back/docs/orders-module/index.md -> docId "back/docs/orders-module"
117
+ back/docs/orders-module/status-axis.md-> docId "back/docs/orders-module/status-axis"
118
+ ```
119
+
120
+ The docId is the path under `knowledge/` without `.md`; `<name>/index.md` collapses back to
121
+ `<name>`, which is what lets a long document be split into a directory of small files without
122
+ changing the id anything already links to. **No file over 12 KB** — the same cap `cms_docs_read`
123
+ applies to a section, so a section can never come back truncated.
124
+
125
+ At startup the server asks GitHub for the commit behind `--knowledge-ref` (one cheap request,
126
+ skipped entirely while the cache is younger than `--knowledge-ttl`, default 1 h), and downloads
127
+ `codeload.github.com/<repo>/tar.gz/<sha>` only when that commit has moved — one request for the
128
+ whole corpus, no API rate limit. The tree is unpacked into `--cache-dir` keyed by commit, so a
129
+ restart is instant and the previous commit is pruned.
130
+
131
+ | situation | what happens |
132
+ |---|---|
133
+ | cache fresh | no network at all |
134
+ | commit unchanged | one API request, no download |
135
+ | commit moved | one API request + one archive download |
136
+ | GitHub unreachable, cache present | the cached commit is used |
137
+ | GitHub unreachable, no cache | falls back to the bundled `operating-rules.md`, and says so in `cms_guide` and `cms_whoami` |
138
+ | `--offline` | cache and bundled rules only, never the network |
139
+
140
+ Which repository and which commit is a configuration choice, not a constant: `--knowledge-repo`
141
+ (`ONEENTRY_MCP_KNOWLEDGE_REPO`, default `ONEENTRY-PLATFORM/oneentry-platform-rules`) and
142
+ `--knowledge-ref`
143
+ (`ONEENTRY_MCP_KNOWLEDGE_REF`, default `main`) — a ref may be a branch, a tag or a commit sha, so a
144
+ stand can be frozen on a known-good corpus. `ONEENTRY_GITHUB_TOKEN` is optional and only raises the
145
+ API rate limit; the repo is public. `cms_whoami` reports the repo, ref and 12-character commit the
146
+ running server actually loaded.
147
+
148
+ ### Reading the docs from a local folder instead
149
+
150
+ For testing, or for editing docs without committing, point the server at a directory:
141
151
 
142
152
  ```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
153
+ ONEENTRY_MCP_KNOWLEDGE_PATH=../oneentry-platform-rules oneentry-mcp-platform
154
+ # or: oneentry-mcp-platform --knowledge-path ../oneentry-platform-rules
145
155
  ```
146
156
 
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.
157
+ Either the clone root or its `knowledge/` directory works the path is probed for a `knowledge`
158
+ subdirectory and used directly if there is none. While it is set, GitHub is never contacted and the
159
+ cache is not consulted, so edits take effect on the next restart with no commit and no push;
160
+ `cms_whoami` reports `source: "local"`. Leave it unset for the default, which is GitHub.
161
+
162
+ A path that cannot be read is a **startup error**, not a fallback: unlike GitHub being unreachable,
163
+ a bad path is a configuration mistake, and degrading silently to the bundled operating rules would
164
+ look like an empty corpus.
165
+
166
+ ### Known limitation: retrieval is single-language
167
+
168
+ Search does not translate. The backend corpus is largely English, so a Russian question will not
169
+ find an English-only document no matter how it is weighted — and the reverse holds too. The fix is
170
+ content, not code: give such a document a short summary section in the other language, or ask in the
171
+ language the document is written in.
153
172
 
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.
173
+ ### Publishing CMS docs into it
156
174
 
157
- The catalog is built from two sources, because neither alone is enough:
175
+ The knowledge repo is what the server reads; the CMS repos remain where the docs are written.
176
+ To move them across:
177
+
178
+ ```bash
179
+ npm run publish:knowledge -- --back ../dnk-back --front ../dnk-front --out ../oneentry-platform-rules
180
+ ```
181
+
182
+ It copies `.claude/docs`, `.claude/tests` and `.claude/rules` from both checkouts, splits anything
183
+ over 12 KB at `##` boundaries, and reports files in the target it did not produce. It **does not
184
+ commit or push** — that diff is the moment internal text becomes public, and a human owns it.
185
+ `.claude/fixes` and the CMS `CLAUDE.md` files are deliberately not published.
186
+
187
+ ## Where the catalog comes from
188
+
189
+ Two sources, because neither alone is enough:
190
+
191
+ 1. `GET {baseUrl}/workflows/nodes/admin-api/swagger.json?langCode=en_US` — paths, operation ids and
192
+ schemas, straight from the stand you are pointed at. Cached per base URL, so an unreachable
193
+ stand still yields the last known catalog.
194
+ 2. `data/permissions.json` — the `@GrantByPermission` decorators, which never reach swagger.
195
+ Regenerate it when the CMS changes its permissions:
196
+
197
+ ```bash
198
+ npm run sync:permissions -- --back ../dnk-back
199
+ ```
158
200
 
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.
201
+ That pass walks base classes too, since Nest derives the operation id from the *registered*
202
+ controller while the handler often lives in a parent.
163
203
 
164
- Two classes of inconsistency in dnk-back are surfaced as catalog `warnings` (visible in
165
- `cms_whoami`) rather than silently swallowed:
204
+ Inconsistencies are surfaced as catalog `warnings` (visible in `cms_whoami`) rather than swallowed:
205
+ paths outside `/api/admin` (dropped — this server exposes the Admin API only), operations with no
206
+ `operationId`, handlers whose permission is known but which the stand does not expose, and
207
+ permissions required by controllers but absent from `AdminPermissionsEnum` — no admin can hold
208
+ those, so such endpoints answer 403 forever.
166
209
 
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.
210
+ If the stand does not serve its swagger at all, the catalog is **empty** and says so loudly. Empty
211
+ is safe there is nothing to call — but silence would read as "the CMS has no such endpoint".
170
212
 
171
213
  ### The swagger is not valid JSON Schema
172
214
 
173
- 311 `type` values in the source swagger are TypeScript expressions (`Record<string, any>`,
215
+ Many `type` values in the source swagger are TypeScript expressions (`Record<string, any>`,
174
216
  `CommonLocalizeInfos`, `AttributesSets`, `image`, `date`). The build normalizes what it can and
175
217
  marks the rest `"x-loose": true` with the original under `x-source-type`. For loose fields the
176
218
  `example` is the contract, and client-side body validation is deliberately advisory — the server