Command Palette

Search for a command to run...

UnylyUnyly
Browse all

OntoPortal Server

FreeNot checked

Enables AI agents to search, explore, and interact with ontologies and semantic resources on AgroPortal via the OntoPortal ecosystem.

GitHubEmbed

About

Enables AI agents to search, explore, and interact with ontologies and semantic resources on AgroPortal via the OntoPortal ecosystem.

README

An MCP server that exposes the AgroPortal / OntoPortal API as tools for LLM clients (Claude Desktop, Claude Code, …). It is a thin wrapper around the ontologies_api_client Ruby gem, served with the official mcp gem over stdio (for local clients) or Streamable HTTP / SSE (for remote deployment).

Tools

Tool Description
search_classes Full-text search over ontology classes/concepts. Returns label, URI, source ontology, and definition. Supports restricting to specific ontologies and (optionally) federated search across other OntoPortal instances.
list_ontologies List the ontologies available on the portal, optionally filtered by a case-insensitive match on acronym or name. Returns acronym, name, and URI. Supports federation.
get_class Fetch a single class/concept by its URI within an ontology. Returns preferred label, definition(s), synonyms, obsolete status, and whether it has children. Accepts a language code (default en).
get_submission Get an ontology submission's metadata: version, status, format, dates, license, homepage/documentation, namespace & version IRI, natural languages, keywords, abstract, and description. Defaults to the latest submission; pass submission_id for a specific version.
list_submissions List ontology submissions with their metadata. Pass an ontology acronym for that ontology's full version history (newest first), or omit it to get the latest submission of every ontology on the portal — the quickest way to gather metadata across many ontologies at once. Supports a limit for the portal-wide listing.
get_metrics Get size/structure metrics for a submission: class, individual, and property counts plus hierarchy stats (max depth, max/average child count). Defaults to the latest submission. (For SKOS vocabularies, concepts count as individuals.)

More tools (e.g. search_ontologies, mappings, notes) can be added under lib/agroportal_mcp/tools/ and registered in TOOLS (lib/agroportal_mcp.rb).

Requirements

  • Ruby ≥ 2.7 (this project was set up with Homebrew Ruby at /usr/local/opt/ruby/bin/ruby).
  • A checkout of ontologies_api_ruby_client as a sibling directory (the Gemfile references it via path: '../ontologies_api_ruby_client').
  • An AgroPortal API key (create one from your AgroPortal account page).

Install

export PATH="/usr/local/opt/ruby/bin:$PATH"
cd ontologies_api_mcp
bundle install

Configuration

The server reads its connection settings from the environment:

Variable Required Default Notes
AGROPORTAL_API_KEY yes Your API key.
AGROPORTAL_REST_URL no https://data.agroportal.eu Point at another OntoPortal API to target a different portal.
AGROPORTAL_FEDERATED_PORTALS no Enables the federate option. Space-separated name url apikey triples, comma-separated: "ecoportal https://data.ecoportal.lifewatch.eu KEY1, biodivportal https://data.biodivportal.gfbio.dev KEY2".
AGROPORTAL_MCP_BEARER_TOKEN no HTTP transport only. When set, every request must send Authorization: Bearer <token>; unset means no auth (a warning is logged at boot).
AGROPORTAL_MCP_STATELESS no false HTTP transport only. true makes each request self-contained (no sessions / no server→client SSE stream), which is simpler to scale horizontally.
AGROPORTAL_MCP_REQUIRE_USER_APIKEY no false HTTP transport only. true rejects requests without an X-Agroportal-User-Apikey header (multi-tenant). When false, such requests fall back to AGROPORTAL_API_KEY.

Smoke test

You can drive the stdio protocol by hand:

export AGROPORTAL_API_KEY=your_key
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_classes","arguments":{"query":"wheat","pagesize":3}}}' \
  | /usr/local/opt/ruby/bin/ruby bin/agroportal_mcp

Register with an MCP client

Claude Code (CLI)

claude mcp add agroportal \
  --env AGROPORTAL_API_KEY=your_key \
  -- /usr/local/opt/ruby/bin/ruby \
     /Users/maboukerfa/code/agroportal/ontologies_api_mcp/bin/agroportal_mcp

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "agroportal": {
      "command": "/usr/local/opt/ruby/bin/ruby",
      "args": [
        "/Users/maboukerfa/code/agroportal/ontologies_api_mcp/bin/agroportal_mcp"
      ],
      "env": {
        "AGROPORTAL_API_KEY": "your_key",
        "AGROPORTAL_REST_URL": "https://data.agroportal.eu"
      }
    }
  }
}

The entrypoint pins BUNDLE_GEMFILE to this project, so it can be launched from any working directory.

Remote deployment (Streamable HTTP / SSE)

To serve the same tools to remote clients, run the MCP Streamable HTTP transport instead of stdio. It uses SSE (text/event-stream) for streaming server responses, and replaces the deprecated standalone HTTP+SSE transport. config.ru mounts the server as a Rack app; install the http group (bundle install does this by default) and run it under Puma:

export AGROPORTAL_API_KEY=your_key
export AGROPORTAL_MCP_BEARER_TOKEN=$(openssl rand -hex 32)   # recommended
bundle exec puma config.ru -p 9292

The endpoint serves JSON-RPC at the root path. Smoke-test the handshake (drop the Authorization header if you left the token unset):

# 1) initialize — capture the Mcp-Session-Id from the response headers
curl -sN -i -X POST http://127.0.0.1:9292/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "Authorization: Bearer $AGROPORTAL_MCP_BEARER_TOKEN" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}'

# 2) tools/list — reuse the session id from step 1 (response streams back as SSE)
curl -sN -X POST http://127.0.0.1:9292/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "Authorization: Bearer $AGROPORTAL_MCP_BEARER_TOKEN" \
  -H 'Mcp-Session-Id: <id-from-step-1>' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Operational notes:

  • Terminate TLS in front. Run it behind a reverse proxy (nginx/Caddy) or load balancer that handles HTTPS — the bearer token is only as safe as the transport.
  • Don't expose it unauthenticated. With AGROPORTAL_MCP_BEARER_TOKEN unset the endpoint is open (and warns at boot). Set it, or front it with an authenticating proxy.
  • Concurrency. In the default stateful mode each connected client holds a long-lived SSE stream open, which ties up a Puma thread; size --threads (or set AGROPORTAL_MCP_STATELESS=true) to match expected concurrency. For many concurrent SSE streams, Falcon (fiber-based) scales better and serves the same config.ru — add gem 'falcon' to the :http group, then bundle exec falcon serve -b http://0.0.0.0:9292.

Register the remote server with Claude Code:

claude mcp add --transport http agroportal https://your-host.example/ \
  --header "Authorization: Bearer your_token"

Multi-tenant: per-user API keys

Host one shared instance and let each user authenticate with their own AgroPortal key. The server uses its own AGROPORTAL_API_KEY as the base credential and forwards each caller's key as userapikey, so the API attributes requests — permissions, rate limits, private ontologies — to that user.

export AGROPORTAL_API_KEY=server_app_key          # must be allowed to use userapikey (see below)
export AGROPORTAL_MCP_REQUIRE_USER_APIKEY=true     # reject requests with no user key
export AGROPORTAL_MCP_STATELESS=true               # recommended for multi-tenant
bundle exec puma config.ru -p 9292

Each user registers with their own key in the X-Agroportal-User-Apikey header:

claude mcp add --transport http agroportal https://mcp.agroportal.eu/ \
  --header "X-Agroportal-User-Apikey: <their_personal_key>"

The key is read per request and forwarded by the client's user_apikey middleware, producing Authorization: apikey token=<server>&userapikey=<user>. Thread#[] is fiber-local, so requests stay isolated under both Puma and Falcon.

Requirements and notes:

  • The server's AGROPORTAL_API_KEY must be permitted to use userapikey — an admin/app account, not an ordinary user key. (You run AgroPortal, so you can mint one; confirm your API honours userapikey for it.)
  • HTTPS is mandatory — users send their personal key on every request.
  • Keep the client cache off (the default). A shared cache could serve one user's results, including private ontologies, to another.
  • Without AGROPORTAL_MCP_REQUIRE_USER_APIKEY, a request that omits the header falls back to the server key; set it to reject those instead.
  • The optional AGROPORTAL_MCP_BEARER_TOKEN gate is independent and can be layered on top for a deployment-wide secret.

Layout

bin/agroportal_mcp                     # executable stdio entrypoint (local clients)
config.ru                              # Rack entrypoint: Streamable HTTP / SSE (remote)
lib/agroportal_mcp.rb                  # build_server + run (stdio) / http_transport (SSE)
lib/agroportal_mcp/middleware.rb       # Rack middlewares: bearer gate + per-user apikey
lib/agroportal_mcp/client_setup.rb     # env -> LinkedData::Client config (+ Rails.cache shim)
lib/agroportal_mcp/tools/              # one file per MCP tool
  search_classes.rb

from github.com/maboukerfa/ontoportal-mcp

Installing OntoPortal Server

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/maboukerfa/ontoportal-mcp

FAQ

Is OntoPortal Server MCP free?

Yes, OntoPortal Server MCP is free — one-click install via Unyly at no cost.

Does OntoPortal Server need an API key?

No, OntoPortal Server runs without API keys or environment variables.

Is OntoPortal Server hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install OntoPortal Server in Claude Desktop, Claude Code or Cursor?

Open OntoPortal Server on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare OntoPortal Server with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All ai MCPs