OSIRIS MCP
OSIRIS MCP is a read-only Model Context Protocol server for controlled access to research information stored in OSIRIS.
The project is intentionally split into two layers:
- OSIRIS remains responsible for data access and authorization.
- This MCP server exposes a small allowlist of typed, auditable tools.
The current implementation is an initial development scaffold. Before exposing it publicly, review the deployment end to end, including TLS, network access, logging, rate limits, and the scopes granted to its dedicated API client.
Installation
Python 3.12 is required. Run the published package without installing it globally:
uvx --from osiris-mcp osiris-mcp
Alternatively, install the command into an isolated environment:
pipx install osiris-mcp
osiris-mcp
The server uses the stdio transport by default and reads its configuration from
environment variables or a .env file in the current directory. At minimum,
set OSIRIS_BASE_URL and OSIRIS_API_KEY; dedicated OSIRIS API clients should
also set OSIRIS_CLIENT_ID. See Configuration for the full
setup and the distinction between downstream OSIRIS credentials and inbound MCP
authentication.
Development setup
uv sync
uv run pytest
Start the local stdio server:
uv run osiris-mcp
Local Docker container
The included Compose configuration runs OSIRIS MCP as a persistent local Streamable HTTP service. It requires Docker but no local Python installation.
cp .env.example .env
# Configure the OSIRIS URL, client ID, API key, and source URL in .env.
docker compose up --build -d
The MCP endpoint is then available at http://127.0.0.1:8765/mcp and the
minimal process health check at http://127.0.0.1:8765/health. Stop it with:
docker compose down
The published port is deliberately bound to 127.0.0.1. The default
OSIRIS_MCP_AUTH_MODE=none is suitable only for this loopback setup and must
never be exposed on a LAN, through a reverse proxy, or on the public internet.
DNS-rebinding protection additionally permits only local Host and Origin values,
but it is not a substitute for authentication or the loopback binding.
The image runs as an unprivileged user with a read-only filesystem, all Linux
capabilities removed, and no-new-privileges enabled. Its health response does
not test OSIRIS or reveal configuration. The build also places the Corresponding
Source in /usr/src/osiris-mcp inside the image.
On Docker Desktop, services running directly on the host are reachable from the
container as host.docker.internal. The included local OAuth overlay also maps
the osiris.test virtual host to the Docker host.
For the local Keycloak setup described below, keep its public issuer at
http://127.0.0.1:8080/realms/osiris so browser discovery remains stable, and
start OSIRIS MCP with:
docker compose -f compose.yaml -f compose.local-oauth.yaml up --build -d
The overlay changes the container's back-channel introspection URL to
host.docker.internal, while retaining 127.0.0.1:8080 as its HTTP Host
header. This is necessary because local Keycloak tokens use the public issuer
hostname and Keycloak otherwise treats them as inactive during introspection.
The overlay also sets the public MCP URL to the published port 8765 and
explicitly permits unencrypted introspection on this trusted local Docker
bridge. It must not be used for a production deployment. Stop this stack with:
docker compose -f compose.yaml -f compose.local-oauth.yaml down
The command-line entry point supports these transport settings:
OSIRIS_MCP_TRANSPORT:stdio(default) orstreamable-httpOSIRIS_MCP_HOST:127.0.0.1,localhost, or0.0.0.0OSIRIS_MCP_PORT: internal HTTP port, default8000OSIRIS_MCP_PATH: MCP path, default/mcpOSIRIS_MCP_PUBLISHED_PORT: host port used by Compose, default8765OSIRIS_MCP_AUTH_MODE:none,api-key, oroauth
For orchestrator-managed secrets, omit OSIRIS_API_KEY and set
OSIRIS_API_KEY_FILE to a mounted secret file instead. Configuring both is
rejected to avoid ambiguous secret precedence.
Inbound MCP authentication
Authentication protects access from an MCP client to this server. It is
separate from OSIRIS_API_KEY, which the server uses for its downstream calls
to OSIRIS. Authentication applies only to Streamable HTTP; stdio relies on the
security boundary of the process that launches it.
OAuth/OIDC resource-server mode
oauth is the production mode for remotely reachable installations. OSIRIS MCP
does not implement login, consent, or token issuance. An external authorization
server such as Keycloak or another institutional provider with an RFC 7662
introspection endpoint does that. OSIRIS MCP validates every access token
through that endpoint and verifies activity, expiry, issuer, audience, and the
required scopes.
OSIRIS_MCP_TRANSPORT=streamable-http
OSIRIS_MCP_AUTH_MODE=oauth
OSIRIS_MCP_PUBLIC_URL=https://mcp.example.org/mcp
OSIRIS_MCP_OAUTH_ISSUER_URL=https://login.example.org/realms/osiris
OSIRIS_MCP_OAUTH_INTROSPECTION_URL=https://login.example.org/realms/osiris/protocol/openid-connect/token/introspect
OSIRIS_MCP_OAUTH_CLIENT_ID=osiris-mcp
OSIRIS_MCP_OAUTH_CLIENT_SECRET_FILE=/run/secrets/oauth_client_secret
OSIRIS_MCP_OAUTH_REQUIRED_SCOPES=osiris:read
The public URL must identify the exact MCP endpoint, including its path. HTTPS
is mandatory outside localhost. By default the token audience must equal this
URL; set OSIRIS_MCP_OAUTH_AUDIENCE only when the identity provider uses a
different API audience identifier. The MCP SDK publishes RFC 9728 Protected
Resource Metadata and returns standards-compliant 401 and 403 challenges,
allowing capable clients to discover the authorization server automatically.
The introspection client secret is an identity-provider credential and should
be mounted as a secret file. It is never forwarded to OSIRIS. The access token
received from the MCP client is likewise never passed to OSIRIS; downstream
requests always use the dedicated OSIRIS_API_KEY.
HTTP introspection on a non-loopback hostname is rejected by default. The
OSIRIS_MCP_OAUTH_ALLOW_INSECURE_INTROSPECTION escape hatch exists only for the
local Docker bridge overlay. Production introspection must use HTTPS.
When a private back-channel URL reaches the same authorization server through a
different hostname, OSIRIS_MCP_OAUTH_INTROSPECTION_HOST_HEADER can explicitly
preserve the public issuer's HTTP host. Leave it unset unless the authorization
server or reverse proxy requires this routing behavior.
Static API-key mode
api-key is a simpler option for a controlled internal network or a single
trusted client that supports fixed HTTP headers. Generate a random secret of at
least 32 characters and configure the client to send it on every MCP request:
OSIRIS_MCP_TRANSPORT=streamable-http
OSIRIS_MCP_AUTH_MODE=api-key
OSIRIS_MCP_API_KEY_FILE=/run/secrets/osiris_mcp_api_key
Authorization: Bearer <OSIRIS_MCP_API_KEY>
For local Compose, place the key in the ignored secrets/ directory and add
the mount to compose.override.yaml:
services:
osiris-mcp:
volumes:
- ./secrets/osiris_mcp_api_key:/run/secrets/osiris_mcp_api_key:ro
This mode uses constant-time secret comparison, rejects missing or malformed
credentials with 401, and leaves /health public. It intentionally publishes
no OAuth discovery metadata and therefore does not provide interactive login,
individual user identities, scopes, or automatic token rotation. Use it behind
TLS and network restrictions; prefer OAuth for multi-user or public deployments.
none remains available for stdio and loopback-only development. The server
rejects api-key or oauth configuration with stdio so operators cannot assume
that a pipe is protected by HTTP authentication.
Open it in the MCP Inspector:
uv run mcp dev src/osiris_mcp/server.py:mcp
Configuration
Copy .env.example to .env and adjust it for a development OSIRIS instance.
Never commit .env or real API keys.
Set OSIRIS_MCP_SOURCE_URL to the public repository containing the exact source
code of the deployed version. This value is exposed by server_info. Operators
who make a modified version available over a network must point it to the
Corresponding Source of that modified version as required by AGPL section 13.
For a dedicated MCP client, configure both OSIRIS_CLIENT_ID and
OSIRIS_API_KEY. In OSIRIS, allow the client to use the MCP API area and grant
only the read permissions required by the enabled tools. The legacy global API
key remains supported without a client ID, but is unrestricted and should be
avoided for new installations.
The available read-only tools are:
server_info: shows non-sensitive server, connection, license, and source-code information.get_instance_info: describes the connected OSIRIS installation, enabled features, catalog sizes, and supported project filters.list_units: resolves human-readable organizational unit names to the exact IDs used by this OSIRIS installation.list_topics: resolves research topic names to exact IDs and explicitly reports when the installation has no topic catalog.list_activity_types: lists the exact activity category and subtype IDs used by the installation.search_activities: searches activities while returning only a compact citation-centered evidence bundle.get_activity: retrieves the same compact representation for one activity.search_people: resolves names, usernames, aliases, and ORCIDs to exact OSIRIS person IDs without exposing contact or account metadata.get_person: retrieves a compact research profile for one exact username.search_experts: searches curated expertise and research fields and returns explicit evidence for every match. When enabled, OpenAlex publication topics are included as lower-priority evidence.search_projects: searches projects through the dedicated/api/mcp/projectsOSIRIS endpoint using fixed filters and a fixed field allowlist.get_project: retrieves the allowlisted details of one project by ID.
Activity date filters use date_field=start by default; for example,
from_date=2026-09-01 and to_date=2026-09-30 select activities starting in
September 2026. Set date_field=end to select activities completed in that
period, such as completed theses. Activity searches include only affiliated
records and exclude Online-ahead-of-print records by default. Exceptional
searches can opt in with include_unaffiliated=true or
include_online_ahead_of_print=true. Project searches can be narrowed by a
free-text query, an active_on date in YYYY-MM-DD format, exact status, topic,
organizational unit, and result limit.
Topic and unit filters always require exact instance-specific IDs. Clients
should obtain them with list_topics and list_units rather than guessing.
Complete result lists
List and search tools return bounded pages so a large OSIRIS installation cannot
overflow a single MCP response. Every page contains count, total, offset,
limit, has_more, and next_offset. To obtain a complete result list, keep
the original filters unchanged and pass the returned next_offset into the next
call until has_more is false. Organizational-unit and topic resources follow
the same pagination internally and therefore return their complete catalogs.
The same discovery information is available as MCP resources for clients that use resource discovery:
osiris://instanceosiris://unitsosiris://topicsosiris://activity-types
The recommended client flow is to read get_instance_info first, resolve any
topic or unit mentioned by a user through the corresponding catalog tool, and
only then pass the returned ID to search_projects. Tools mirror the resources
because some MCP hosts do not automatically load resources into the model's
context.
OSIRIS provides these dedicated endpoints to the MCP adapter:
GET /api/mcp/instanceGET /api/mcp/unitsGET /api/mcp/topicsGET /api/mcp/activity-typesGET /api/mcp/activitiesGET /api/mcp/activities/{id}GET /api/mcp/personsGET /api/mcp/persons/{username}GET /api/mcp/expertsGET /api/mcp/projectsGET /api/mcp/projects/{id}
OSIRIS authenticates this adapter as a dedicated API client. Client secrets are stored as hashes, can be rotated or disabled independently, and are restricted to the MCP API area and explicitly granted read permissions.
Safe errors and request IDs
Every OSIRIS MCP API request receives a server-generated identifier in the form
req_<32 hexadecimal characters>. It is returned in the X-Request-ID header
for successful and unsuccessful responses. Error responses also contain the
same value as request_id in their JSON body.
Expected validation and not-found responses remain machine-readable. Unexpected PHP errors are logged inside OSIRIS with their request ID and MCP endpoint, while the caller receives only a generic HTTP 500 response—never a stack trace, source path, database error, or raw exception message. Buffered warnings and stray output are discarded before the JSON response is sent.
The Python adapter does not forward OSIRIS error messages or rejected response values to the MCP client. It emits a stable description and the validated request ID, when one was received. This gives administrators a useful support reference without exposing server details to the language model. Transport failures are reported generically because no server-side request ID exists. Expected errors are explicitly marked as safe MCP tool errors so the model can read this description. Unexpected exceptions remain masked by the MCP runtime. If local input validation fails before an HTTP request is made, the error states that OSIRIS was not called and therefore no request ID exists. HTTPX request logging is restricted to warnings and errors because complete request URLs can contain names, search terms, or other sensitive query parameters.
Compact activity representation
Activity documents can contain extensive editing history, metrics, external metadata, and HTML renderings. The MCP API does not expose those raw fields. Search and detail results share one small contract containing only:
- ID, exact type and subtype, and title
- start and end date
- linked OSIRIS persons and organizational units
- the plain-text
citationrendered by OSIRIS - stable identifiers such as DOI or PubMed ID, when present
- affiliation and Online-ahead-of-print status
- optional bibliometric values, including their available reference or retrieval dates
- a source URL added by the MCP adapter
Free-text search may inspect the title, abstract, and rendered citation on the OSIRIS side, but the verbose source fields are not returned to the model. Bibliometric values are contextual evidence and must not be treated as a standalone measure of the quality of a publication or researcher.
Person and expertise search
Identity resolution and expertise discovery are separate operations.
search_people uses the OSIRIS person search field and returns only name,
position, current units, active status, and profile URL. get_person adds ORCID,
expertise, research interests, OSIRIS topics, and the sanitized research profile.
When a unit filter is supplied, only assignments active on the current date are
matched. A missing or null start date has no lower bound; a missing or null end
date has no upper bound.
search_experts searches only curated expertise, research interests, research
profiles, and OSIRIS research topics. It deliberately does not search general
biographies. If the Research Spectrum feature is enabled, matching OpenAlex
topics from affiliated publications are added as clearly identified evidence
with a lower relevance weight than curated profile data.
Email addresses, phone numbers, gender, login history, account roles, internal IDs, biographies, social profiles, and user-interface settings are never part of these MCP responses.
License
OSIRIS MCP is free software licensed under the GNU Affero General Public License,
version 3 or any later version (AGPL-3.0-or-later). You may use, modify,
distribute, and commercially operate the software under the conditions of that
license. In particular, operators of a modified version that users interact with
remotely over a network must offer those users access to the Corresponding Source
of the deployed version.
Copyright © 2026 Julia Koblitz, OSIRIS Solutions GmbH.
See LICENSE for the complete license text and CONTRIBUTING.md for contribution requirements. This license applies to the standalone Python connector in this repository; it does not by itself change the license of the separate OSIRIS Core repository.
Release files for osiris-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| osiris_mcp-0.1.0.tar.gz | 43.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| osiris_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.6 kB
Release files / osiris_mcp-0.1.0.tar.gz
| Download URL | osiris_mcp-0.1.0.tar.gz |
|---|---|
| Size | 43.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b74f6bda4ef13d938c7b7916c80daf8d827ec1f3f9adf43e8ea01b888b252fc5
|
|
BLAKE2b-256 checksum How to use checksums |
f37d6fd4d70b3d310b47d7f2ddf4659651a4db66d42ede249858a5806935f3af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / osiris_mcp-0.1.0-py3-none-any.whl
| Download URL | osiris_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 37.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1de35ec9b1f59371445fa54537ddb63d361a5f906114d4727641ff14201a1e7a
|
|
BLAKE2b-256 checksum How to use checksums |
d87a23af9ffcbe5aa14ec91976b563858b8ec5a9b22dbb81948894f1cd2b683a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency log