ojs-mcp
An MCP server for the authenticated REST API of Open Journal Systems (OJS 3.5/3.6). Connected to an MCP client (Claude Desktop, Claude Code, and others), it gives the model access to a journal's submissions, reviews, issues, and editorial statistics — and, with writes explicitly enabled, also to making editorial decisions, publishing, and editing metadata.
Verified against a live OJS 3.5
Every tool has been exercised end-to-end against a real OJS 3.5.0-5
instance — the sixteen read tools, the ojs_request escape hatch, and all
five write tools. The write tools were checked by reading back the state
OJS actually ended up in, not by trusting the status code: an announcement
created, publication metadata edited, a submission carried from the
submission stage to production by two editorial decisions, then published
and unpublished again.
That instance is reproducible. demo/
holds the Docker stack, the fictional seed content, and the two scripts that
drive this server against it as a real MCP client — check_api.py for the
read tools, check_writes.py for the write tools.
Three things have not been exercised against a live server, and still
rest on what the PKP source (pkp-lib, pkp/ojs) says rather than on
observed behavior: login/password authentication
(OJS_USERNAME/OJS_PASSWORD), network mode (OJS_MCP_TRANSPORT=http),
and OJS 3.6 — only 3.5 has been tested. The unit test suite likewise runs
entirely against stubbed HTTP responses (via respx).
Writes change real journal data, which is why OJS_ALLOW_WRITES=1 is off by
default. Verify each tool's effect on a test journal before pointing it at
production.
Quick start
OJS_BASE_URL=https://journals.your-university.edu OJS_API_TOKEN=your-token uvx ojs-mcp
No separate install step needed — uv
downloads and runs the package on first launch. In practice your MCP
client calls this command for you, using the configuration format from
the section below.
Before you start — this won't work without two things
The OJS REST API has no anonymous read access. For the server to be able to connect at all, both of these must be true on the OJS instance side:
api_key_secretset inconfig.inc.php— without it, API tokens don't work at all (OJS responds with a 500 error to every request that carries a token). This must be done by the OJS server administrator; it can't be worked around from the outside.- An account with a role in the specific journal — merely having an OJS account is not enough. Almost every API endpoint requires some role (manager, editor, reviewer...); an account with no role in the given journal gets denied (401) on almost every call.
A third condition applies to instances served by Apache — including the
official pkpofficial/ojs Docker images: the web server must forward the
Authorization header to PHP. Apache does not do that on its own, and when
it doesn't, OJS answers 401 to every request carrying a token, with a
response indistinguishable from an anonymous one. The one-line fix, and how
to tell this case apart from a genuine permission problem, are in the
authentication documentation.
Without these conditions the server will start, but every tool that reaches into OJS will return an authentication error. Details, including the login/password alternative and its limitations, are in docs/authentication.md.
Getting a token
A logged-in user generates an API token in their own OJS profile: User
Profile → API Key (only available once the instance administrator has
set api_key_secret — see above). The token acts with that account's
permissions, so its scope is whatever roles that account holds in the
given journal.
Example MCP client configuration
{
"mcpServers": {
"ojs": {
"command": "uvx",
"args": ["ojs-mcp"],
"env": {
"OJS_BASE_URL": "https://journals.your-university.edu",
"OJS_JOURNAL": "my-journal",
"OJS_API_TOKEN": "paste-your-ojs-profile-token-here"
}
}
}
}
OJS_JOURNAL is optional — leave it out if the instance serves several
journals and you'd rather pick one with the journal parameter on each
call.
Where to actually paste this configuration
In Claude Desktop: Settings → Developer → Edit Config opens (and, on
first use, creates) the claude_desktop_config.json file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Paste the snippet above under the mcpServers key — if the file already
has other servers configured, add the "ojs" key alongside them, don't
overwrite the whole file — save and restart Claude Desktop. Other
desktop MCP clients have their own place for this configuration (e.g.
Claude Code reads it via claude mcp add or from an .mcp.json file) —
check their documentation; the shape of the env section above is the
same across all of them.
This step disappears entirely when installing from the MCPB bundle
(see below) — there, the instance address, journal, and token are filled
in through a form in the client's UI, with no manual JSON editing at
all. That's a good reason to reach for the bundle instead of uvx if
editing a configuration file by hand isn't your thing.
Environment variables (summary)
| Variable | Required | Description |
|---|---|---|
OJS_BASE_URL |
yes | The OJS instance address, exactly as it works in the browser. |
OJS_JOURNAL |
no | The journal shortcut — skips the journal parameter on every call. |
OJS_API_TOKEN |
no* | The token from a user's profile. Takes precedence over login/password. |
OJS_USERNAME / OJS_PASSWORD |
no* | Form-based login — won't work with reCAPTCHA/ALTCHA. |
OJS_ALLOW_WRITES |
no | 1 exposes the tools that modify journal data (hidden by default). |
* — either OJS_API_TOKEN or the OJS_USERNAME/OJS_PASSWORD
pair is required (in stdio mode). The full list, including the
network-mode variables (OJS_MCP_TRANSPORT and others), is in
docs/configuration.md.
Alternative to uvx: the MCPB bundle
For desktop MCP clients that support the
MCP Bundle (.mcpb)
format — the installer file is attached to every release under
Releases. Installation
happens through the client's UI, configuration through a form instead of
manual JSON editing; no Python or uv installation required — the
bundle pulls its own dependencies on first run.
Documentation
Full documentation (installation, configuration, authentication, tool list, multi-tenant hosting): https://mpasternak.github.io/ojs-mcp/
License
MIT. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ojs_mcp-0.2.1.tar.gz.
File metadata
- Download URL: ojs_mcp-0.2.1.tar.gz
- Upload date:
- Size: 107.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d5a702055d7e0452f452c1a53998a0cba0d745903219b227f01f981737c551b
|
|
| MD5 |
0c66fb6fd4a6c64295ec635de5df7d3e
|
|
| BLAKE2b-256 |
84e2f63dc0963f2200f73a156fefd257c618e158255400205ad1a1e939a61614
|
Provenance
The following attestation bundles were made for ojs_mcp-0.2.1.tar.gz:
Publisher:
release.yml on mpasternak/ojs-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ojs_mcp-0.2.1.tar.gz -
Subject digest:
1d5a702055d7e0452f452c1a53998a0cba0d745903219b227f01f981737c551b - Sigstore transparency entry: 2781945651
- Sigstore integration time:
-
Permalink:
mpasternak/ojs-mcp@bb2cde1e8f81463c564efcdd1d540e7ac877046b -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/mpasternak
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb2cde1e8f81463c564efcdd1d540e7ac877046b -
Trigger Event:
push
-
Statement type:
File details
Details for the file ojs_mcp-0.2.1-py3-none-any.whl.
File metadata
- Download URL: ojs_mcp-0.2.1-py3-none-any.whl
- Upload date:
- Size: 76.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d7a33a2861f1750cd998f1aa94b12254e6ea1a36a69e254d06cd6605ccddd92
|
|
| MD5 |
a22da994f5f58dbfb3aac858bbf177f9
|
|
| BLAKE2b-256 |
e764309f8b06a690b51828be60d72eaf6862997fa08ccbca57b6999808755f0e
|
Provenance
The following attestation bundles were made for ojs_mcp-0.2.1-py3-none-any.whl:
Publisher:
release.yml on mpasternak/ojs-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ojs_mcp-0.2.1-py3-none-any.whl -
Subject digest:
2d7a33a2861f1750cd998f1aa94b12254e6ea1a36a69e254d06cd6605ccddd92 - Sigstore transparency entry: 2781945803
- Sigstore integration time:
-
Permalink:
mpasternak/ojs-mcp@bb2cde1e8f81463c564efcdd1d540e7ac877046b -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/mpasternak
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@bb2cde1e8f81463c564efcdd1d540e7ac877046b -
Trigger Event:
push
-
Statement type: