npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oneentry/mcp-platform-server

v0.1.14

Published

MCP server that lets an AI agent operate the OneEntry Admin API, grounded in the project's own rules

Readme

@oneentry/mcp-platform-server

MCP server that lets an AI agent operate the OneEntry Admin API — grounded in published operating rules rather than in guesswork.

Nothing is frozen into the package. Both halves are fetched at runtime and cached:

  • Knowledge. Small markdown files from a public GitHub repository (ONEENTRY-PLATFORM/oneentry-platform-rules), chunked into searchable sections. Editing a doc there — through the GitHub UI, a PR, whatever — takes effect on the next restart of any server, with no npm release.
  • Actuation. Every Admin API operation, built from the instance's own API document at GET /api/admin/workflows/nodes/admin-api/swagger.json, with parameter and body schemas, the permission each one requires, and a risk classification — reachable through one generic invoke tool instead of one tool per endpoint. The catalog always describes the API that is actually running, not a snapshot of it.

The one thing still committed here is data/permissions.json: the permission an operation requires is declared in platform code and never reaches the API document, so that map has to be recovered separately.

Only the Admin API is exposed.

Install

npm i -g @oneentry/mcp-platform-server     # or use npx, below

Local mode (agent and instance on the same machine)

Add to .mcp.json in the repo you work from:

{
  "mcpServers": {
    "oneentry-mcp-platform": {
      "command": "npx",
      "args": ["-y", "@oneentry/mcp-platform-server"],
      "env": {
        "ONEENTRY_CMS_BASE_URL": "http://localhost:3003/api/admin",
        "ONEENTRY_CMS_LOGIN": "your-admin-login",
        "ONEENTRY_CMS_PASSWORD": "your-admin-password"
      }
    }
  }
}

Nothing has to be checked out locally. On first run the knowledge repository is downloaded once and cached; later runs re-use the cache and only ask GitHub whether the commit moved, at most once an hour.

The base URL must point at an instance that exposes the Admin API under /api/admin.

Remote mode (hosted, many agents, one instance)

oneentry-mcp-platform --http --port 8931 --audit /var/log/oneentry-mcp-platform-audit.jsonl \
        --base-url https://your-instance.example/api/admin \
        --allowed-origins https://agent.example

Each MCP session carries its own identity, passed as connection headers — never as tool arguments, so a prompt-injected instruction cannot swap identities:

| header | meaning | |---|---| | x-cms-token | pre-issued Admin API access token (preferred) | | x-cms-login + x-cms-password | credentials to log in with |

Sessions never share tokens or confirm tokens. GET /health reports liveness and session count.

The nine tools

| tool | what it does | |---|---| | cms_guide | read first: mode, policy, API map, the order to use the other tools | | cms_docs_search | search the knowledge base, returns sections with snippets | | cms_docs_read | read one section, with its sibling sections listed for paging | | cms_api_search | find operations by keyword / tag / method / mutating | | cms_api_describe | params, body schema, required permission, risk, confirm gating, and curatedBody where a shape has been verified on a live instance | | cms_api_call | execute one operation; dryRun and confirm-gating for mutations | | cms_upload_file | upload one file from this machine as multipart/form-data (local mode only) | | cms_import_file_from_url | fetch one file over http(s) and upload it in a single step | | cms_whoami | mode, base URL, admin + permissions, knowledge commit, catalog state and warnings |

Plus two MCP resources: oneentry://knowledge/mcp/operating-rules and oneentry://knowledge/index.

Uploading files

cms_api_call sends JSON only, and the upload endpoint wants multipart/form-data — which is why the two upload tools exist rather than a note telling the agent to go around the server. They run through the same gate as any other write: --allow=write, the local permission check, dryRun, and the audit line.

Both bound their source, because a path or a URL arrives as a tool argument and a tool argument can be prompt-injected:

| flag | env | default | what it bounds | |---|---|---|---| | --upload-root | ONEENTRY_MCP_UPLOAD_ROOT | the process working directory | cms_upload_file reads nothing outside this directory, symlinks resolved first | | --upload-max-bytes | ONEENTRY_MCP_UPLOAD_MAX_BYTES | 25 MiB | size of one file, checked on disk and on the wire | | --upload-allowed-hosts | ONEENTRY_MCP_UPLOAD_ALLOWED_HOSTS | empty | hosts cms_import_file_from_url may fetch from |

cms_upload_file is refused in remote mode: there is no shared filesystem, and a session-supplied path would read the host's files. cms_import_file_from_url refuses any address resolving to a loopback, private, link-local or carrier-grade-NAT range, re-checks every redirect hop, and in remote mode stays disabled until the operator sets an allowlist.

Pass template — the numeric id of a /template-previews record — on the first upload. Without it the file is stored with no previewLink, nothing reports the omission, and the only repair is uploading the file again.

Write safety

Read-only by default. --allow (or ONEENTRY_MCP_ALLOW) raises it:

| level | GET | POST/PUT/PATCH | DELETE | |---|---|---|---| | read (default) | ✅ | ❌ | ❌ | | write | ✅ | ✅ | ❌ | | destructive | ✅ | ✅ | ✅ confirm-gated |

  • A refusal by level happens before authentication — no HTTP request is made at all.
  • Deletes, and every path in the permanently gated list (immutable-settings, admins, backups, modules, payments/webhook, settings-general, system/captcha-keys, auth/logout/all-users), require a confirm token: the first call returns the token and the current state of the target, fetched via the sibling GET, so a human can see what is about to change. The token is bound to hash(opId + arguments), single-use, valid 5 minutes.
  • Permissions are checked locally first: if the admin lacks the permission the operation declares, the call is refused without being sent.
  • Every non-GET call is appended to a JSONL audit log (arguments are hashed, not stored). Required in remote mode.

Where the knowledge comes from

A public GitHub repository of small markdown files. Layout:

knowledge/
  mcp/operating-rules.md                -> docId "mcp/operating-rules"
  mcp/docs/api/orders.md                -> docId "mcp/docs/api/orders"
  mcp/docs/server/cms-api-call.md       -> docId "mcp/docs/server/cms-api-call"
  mcp/docs/api/orders/index.md          -> docId "mcp/docs/api/orders"

The docId is the path under knowledge/ without .md; <name>/index.md collapses back to <name>, which is what lets a long document be split into a directory of small files without changing the id anything already links to. No file over 12 KB — the same cap cms_docs_read applies to a section, so a section can never come back truncated.

At startup the server asks GitHub for the commit behind --knowledge-ref (one cheap request, skipped entirely while the cache is younger than --knowledge-ttl, default 1 h), and downloads codeload.github.com/<repo>/tar.gz/<sha> only when that commit has moved — one request for the whole corpus, no API rate limit. The tree is unpacked into --cache-dir keyed by commit, so a restart is instant and the previous commit is pruned.

| situation | what happens | |---|---| | cache fresh | no network at all | | commit unchanged | one API request, no download | | commit moved | one API request + one archive download | | GitHub unreachable, cache present | the cached commit is used | | GitHub unreachable, no cache | falls back to the bundled operating-rules.md, and says so in cms_guide and cms_whoami | | --offline | cache and bundled rules only, never the network |

Which repository and which commit is a configuration choice, not a constant: --knowledge-repo (ONEENTRY_MCP_KNOWLEDGE_REPO, default ONEENTRY-PLATFORM/oneentry-platform-rules) and --knowledge-ref (ONEENTRY_MCP_KNOWLEDGE_REF, default main) — a ref may be a branch, a tag or a commit sha, so an instance can be frozen on a known-good corpus. ONEENTRY_GITHUB_TOKEN is optional and only raises the API rate limit; the repo is public. cms_whoami reports the repo, ref and 12-character commit the running server actually loaded.

Reading the docs from a local folder instead

For testing, or for editing docs without committing, point the server at a directory:

ONEENTRY_MCP_KNOWLEDGE_PATH=../oneentry-platform-rules oneentry-mcp-platform
# or: oneentry-mcp-platform --knowledge-path ../oneentry-platform-rules

Either the clone root or its knowledge/ directory works — the path is probed for a knowledge subdirectory and used directly if there is none. While it is set, GitHub is never contacted and the cache is not consulted, so edits take effect on the next restart with no commit and no push; cms_whoami reports source: "local". Leave it unset for the default, which is GitHub.

A path that cannot be read is a startup error, not a fallback: unlike GitHub being unreachable, a bad path is a configuration mistake, and degrading silently to the bundled operating rules would look like an empty corpus.

Known limitation: retrieval is single-language

Search does not translate, and the corpus is English. A question asked in another language will not find an English-only document no matter how it is weighted. The fix is content, not code.

The corpus is hand-authored

The knowledge repository is written for a public audience, one file at a time. There is no import script and one must not be added: bulk-copying internal documentation into it would publish source paths, storage identifiers and tracker ids in a single command, and a repository that is public and mirrored cannot be un-published. npm run publish:knowledge exists only to refuse.

At release time, copy knowledge/mcp/operating-rules.md from the rules repository over this package's knowledge/operating-rules.md — the rules repository is the source, and the bundled seed is never hand-edited.

Where the catalog comes from

Two sources, because neither alone is enough:

  1. GET {baseUrl}/workflows/nodes/admin-api/swagger.json?langCode=en_US — paths, operation ids and schemas, straight from the instance you are pointed at. Cached per base URL, so an unreachable instance still yields the last known catalog.
  2. data/permissions.json — the permission each operation requires, which the API document does not express. Regenerate it when the platform changes its permissions:
npm run sync:permissions -- --back <path to a platform checkout>

That pass walks base classes too, since the operation id is derived from the registered class while the handler often lives in a parent.

Inconsistencies are surfaced as catalog warnings (visible in cms_whoami) rather than swallowed: paths outside /api/admin (dropped — this server exposes the Admin API only), operations with no operationId, operations whose permission is known but which the instance does not expose, and permissions that operations require but no admin can hold — such endpoints answer 403 forever.

If the instance does not serve its API document at all, the catalog is empty and says so loudly. Empty is safe — there is nothing to call — but silence would read as "the platform has no such endpoint".

The API document is not valid JSON Schema

Many type values in the source document are expressions in the platform's own type language (Record<string, any>, CommonLocalizeInfos, AttributesSets, image, date). The build normalizes what it can and marks the rest "x-loose": true with the original under x-source-type. For loose fields the example is the contract, and client-side body validation is deliberately advisory — the instance is the real validator, so a call is never blocked because a loose field could not be checked.

Tests

npm test                 # unit + MCP protocol + remote-mode transport (no instance needed)
npm run test:integration # against a live instance; skips cleanly if unreachable

The integration suite needs ONEENTRY_CMS_BASE_URL pointing at an instance that exposes the Admin API, plus ONEENTRY_CMS_LOGIN/ONEENTRY_CMS_PASSWORD. It walks search → describe → call, asserts a read-only refusal sends nothing, and runs a full create → confirm-gated delete cycle on a throwaway template while checking the audit log.

License

MIT — see LICENSE.