NDEx Rest Server
A production-oriented Docker image bundles the full NDEx stack — PostgreSQL, Keycloak, Apache Solr, MailHog, and the NDEx REST API (Tomcat) — into a single self-contained image.
Key features:
- Monolithic or distributed: run all services in one container, or split them across containers using command-line flags (
--ndex,--postgres,--solr,--keycloak,--mailhog) - Flag-driven startup: enable only the services you need — no environment variables required
- Ephemeral by default: all state lives in the container layer; bind-mount host paths to persist config or data across restarts
- Auto-provisioned credentials: in monolithic mode, randomized credentials are written to
/etc/<svc>.otpon first boot and deleted after 2 hours - Integration tested: a live integration test script (
docker/test/integration-test.sh) validates the ndex API is working in container through full API lifecycle including CX1/CX2 network upload, summary polling, CX2 retrieval, and Solr search
For complete build, run, configuration, and testing instructions, see docker/README.md.
A fully self-contained devcontainer bundle is provided under .devcontainer. It provides PostgreSQL, Keycloak, Solr, MailHog, and the NDEx API (Jetty) built from current source code of ndex-rest into a single container — no external service installation needed.
For complete setup, configuration, testing, and persistence instructions, see .devcontainer/README.md.
NDEx exposes an MCP (Model Context Protocol) server at /mcp/*, enabling AI agents and LLM clients to interact with NDEx networks and folders through a standardized tool interface.
Servlet architecture (separate from the REST API):
- The MCP servlet is registered programmatically at startup by
McpServletContextListenerusing the MCP SDK'sHttpServletStreamableServerTransportProvider— it cannot be declared inweb.xmlbecause the provider uses a builder-only construction pattern - It runs as an independent async servlet alongside the RESTEasy JAX-RS servlet, sharing the same Tomcat/Jetty container but with its own request pipeline which is necessary to support the streaming transport over http that mcp protocol requires and JAX-RS servlet doesn't support.
/mcp/manifestis served by a dedicatedMcpManifestServletand is always publicly accessible — no credentials required/mcp/uploadand/mcp/downloadare served byUploadPreSignedServletandDownloadPreSignedServlet— both bypass auth filtering because they are guarded by single-use pre-signed tokens based an authenticated users(120-second TTL) issued byrequest_network_uploadandrequest_network_download; authorization is enforced at token-issuance time (the calling user must be authenticated), not at transfer time
Authentication (handled by McpAuthFilter before tools are invoked):
All /mcp requests (except /mcp/manifest, /mcp/upload, and /mcp/download) pass through
McpAuthFilter. On success the resolved User object is attached to the servlet request and
propagated into every tool call via MCP transport context.
Auth is attempted in this order:
-
Basic auth (primary) — if
Authorization: Basic <base64>is present it is validated against the NDEx user database. On success the request proceeds. On failure a plain 401 is returned with noWWW-Authenticateheader (no OAuth fallback is triggered). -
Bearer token — if
Authorization: Bearer <token>is present it is validated using the configured OAuth authenticator (Keycloak or Google — see configuration below). On failure a plain 401 is returned. -
No credentials — anonymous is allowed on mcp, it will allow an agent to handshake with mcp server and excehange manifests. However, each specific tool additionally checks authentication vs. anon during invocation as as a pass-through to the underlying api auth requirements that each tool wraps. some tools will allow anon invocation and others wont.
Auth and no-auth tool pathways:
- Auth-required tools (
request_network_upload,delete_network,manage_folder,get_folder,share_network,get_user_networks,get_user_info,set_network_properties,set_network_systemproperties,update_network_profile) perform an explicit auth check — requests with no authenticated user are rejected and get a mcp response with reason of authentication error. - Always-public tools (
get_connection_status,search_network,get_network_summary) never require authentication. - Conditionally-auth tools (
request_network_download) perform a visibility pre-check on requested file, if the target network is PUBLIC or UNLISTED, anonymous callers are allowed if the network is PRIVATE and the caller has no authenticated session, the tool is denied.
OAuth discovery endpoints (/.well-known/oauth-protected-resource):
As of current, the NDEx MCP server will not initiate OAuth 2.1 + PKCE flows for unauthenticated clients. It will accept an existing OAuth Bearer token but that is the extent of support for now.
OAuthProtectedResourceServlet serves RFC 9728 Protected Resource Metadata at both
/.well-known/oauth-protected-resource/mcp and /.well-known/oauth-protected-resource.
The response always includes an authorization_servers field whose value depends on what is
configured in ndex.properties:
ndex.properties setting |
authorization_servers value in response |
|---|---|
USE_KEYCLOAK_AUTHENTICATION=true + KEYCLOAK_ISSUER=<url> |
["<KEYCLOAK_ISSUER value>"] |
USE_GOOGLE_AUTHENTICATION=true |
["https://accounts.google.com"] |
| Neither (default) | [] — empty array |
An empty authorization_servers array explicitly tells MCP clients that no OAuth authorization
server is configured, preventing them from probing /.well-known/oauth-authorization-server/*.
When a Keycloak or Google issuer is present, MCP clients can use that server to obtain Bearer
tokens that the server will then validate via McpAuthFilter.
If your agent is not listed below, consult its documentation for configuring an MCP server with Streamable HTTP transport. The NDEx MCP server URL is:
http://<host>:8080/mcp
For a local Docker container that is http://localhost:8080/mcp. Replace the host and port
for any remote NDEx server.
Prerequisite: In Claude Desktop go to Settings > Extensions > Advanced and enable Use Built-in Node.js for MCP. This is required for the extension to function.
Download ndex-mcp.mcpb from the
releases page. In Claude Desktop go to
Settings > Extensions, click Install Extension, and select the downloaded file.
The NDEx MCP connector will appear in Customize > Connectors. Fill in the host, port, username, and password fields. Leave username/password blank for anonymous read-only access to public networks.
claude mcp add --transport http ndex http://localhost:8080/mcp \
--header "Authorization: Basic $(echo -n 'username:password' | base64)"Or add to ~/.claude.json (user-wide) or .mcp.json (project root):
{
"mcpServers": {
"ndex": {
"type": "http",
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
}
}
}
}dXNlcm5hbWU6cGFzc3dvcmQ= is the base64 encoding of username:password.
Generate yours: echo -n 'username:password' | base64 (macOS/Linux) or
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("username:password"))
(Windows PowerShell). Omit the headers block for anonymous access.
Add to ~/.copilot/mcp-config.json (user-wide, default location — override with COPILOT_HOME):
{
"mcpServers": {
"ndex": {
"type": "http",
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
}
}
}
}dXNlcm5hbWU6cGFzc3dvcmQ= is the base64 encoding of username:password.
Generate yours: echo -n 'username:password' | base64 (macOS/Linux) or
[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("username:password"))
(Windows PowerShell). Omit the headers block for anonymous access.
Alternatively, add the server interactively from within a running copilot session using the /mcp add slash command.
Add to .vscode/mcp.json in the workspace root. The ${input:ndex-token} reference
prompts once and is stored securely by VS Code:
{
"inputs": [
{
"type": "promptString",
"id": "ndex-token",
"description": "NDEx Basic Auth token — base64 of username:password",
"password": true
}
],
"servers": {
"ndex": {
"type": "http",
"url": "http://localhost:8080/mcp",
"headers": {
"Authorization": "Basic ${input:ndex-token}"
}
}
}
}Omit the headers block and inputs array for anonymous access.