The SearchUnify MCP Server (su-mcp) is a lightweight middleware built on the Model Context Protocol (MCP) that enables secure, seamless integration between SearchUnify Cognitive Search and AI assistants such as Claude Desktop, Cursor, and any other MCP-compatible client.
By leveraging su-sdk-js, this server exposes enterprise search and analytics capabilities as MCP tools, allowing LLMs to invoke context-aware search queries, retrieve analytics reports, and power intelligent workflows directly within external environments.
- LLM-Integrated Enterprise Search -- Power GenAI tools like Claude with context-aware, enterprise-grade search from SearchUnify, enabling more relevant, trusted, and explainable responses.
- Search Analytics -- Retrieve analytics reports (top queries, zero-result queries, conversion data, and more) directly through AI assistants.
- Faceted Filtering -- Discover and apply facet/filter options to narrow search results by category, source, index, and other dimensions.
- Multiple Authentication Methods -- Supports API Key, OAuth 2.0 Password Grant, Client Credentials Grant, and tool-based login (link-in-chat) for flexible, enterprise-grade security.
- Dual Transport -- Runs over stdio (for local tools like Claude Desktop) or HTTP (Streamable HTTP for remote/cloud clients), or both simultaneously.
- Plug-and-Play Deployment -- Docker-based setup for rapid configuration, portability, and scalability across environments.
The MCP server exposes 5 tools that AI assistants can invoke:
Performs a search query against your SearchUnify instance and returns relevant results.
| Parameter | Type | Required | Description |
|---|---|---|---|
searchString |
string |
Yes | The search query (3-100 characters). Can be a single word or a sentence. |
aggregations |
array |
No | List of facet filters (from get-filter-options) to narrow results by category, source, etc. Each item has type (string) and filter (string). |
page |
integer |
No | Page number for pagination (1-100). Defaults to 1. |
pageSize |
integer |
No | Number of results per page (1-100). Defaults to 10. |
sortBy |
string |
No | Field to sort results by. Allowed values: _score (relevance) or post_time (date). |
versionResults |
boolean |
No | Whether to use versioning. Defaults to false. |
Aggregation object schema:
| Field | Type | Description |
|---|---|---|
type |
string |
Aggregation/facet type (e.g. documentation_category, _index). |
filter |
string |
Selected filter value (e.g. Search Clients). |
Behavior:
- If the SearchUnify search client has GPT enabled, the tool returns enriched GPT context with links and descriptions.
- Otherwise, it returns standard search hits with title, summary, and URL.
- HTML tags are automatically stripped from results.
Only available on the
/mcp-connectendpoint. Not present on the standard OAuth or header-based endpoints.
Initiates the tool-based login flow for environments where the browser cannot auto-open (e.g. some Claude Desktop setups). Returns a clickable link to the SearchUnify connection form. The user opens the link manually, fills in their instance details, and logs in — after which all other tools become available.
No parameters required.
Returns: A markdown link to the connection form, e.g.:
Click the link below to connect your SearchUnify instance:
[Connect SearchUnify](https://mcp.searchunify.com/mcp-connect/login?s=<session_id>)
After completing login in your browser, let me know and I will continue with your request.
Retrieves available filter/facet options for a search query. Use this tool first to discover what filters are available, then pass the desired filters into the search tool.
| Parameter | Type | Required | Description |
|---|---|---|---|
searchString |
string |
Yes | The search query (3-100 characters). A single word or sentence. |
aggregations |
array |
No | Optional list of currently applied filters, to get contextual filter options for a filtered search. Same schema as search. |
Returns: An array of aggregation groups, each containing:
| Field | Description |
|---|---|
key |
Internal key for the aggregation (e.g. _index, documentation_category). |
label |
Display label for the aggregation. |
order |
Sort order of the aggregation. |
values |
Array of filter values, each with displayName, value, and contentName. |
Retrieves analytics reports from SearchUnify.
| Parameter | Type | Required | Description |
|---|---|---|---|
reportType |
string (enum) |
Yes | Type of analytics report. See supported values below. |
startDate |
string |
Yes | Start date of the report period. |
endDate |
string |
Yes | End date of the report period. |
count |
number |
Yes | Number of records to fetch. |
Supported reportType values:
| Value | Description |
|---|---|
searchQueryWithNoClicks |
Search queries that returned results but received no clicks. |
searchQueryWithResult |
Search queries that returned results. |
searchQueryWithoutResults |
Search queries that returned zero results. |
getAllSearchQuery |
All search queries. |
getAllSearchConversion |
All search conversion data. |
averageClickPosition |
Average click position data per search query. Returns ACP, click count, search count, and session count. |
sessionDetails |
Session activity logs including page views, searches, conversions, and case events. |
Lists all search clients configured in the SearchUnify instance. Returns minimal information for each search client. No parameters required -- the tenant is derived from the authentication credentials.
Returns: An array of search clients, each containing:
| Field | Description |
|---|---|
id |
Search client ID. |
name |
Search client name. |
uid |
Search client unique identifier (UUID). |
search_client_type |
Type of search client (e.g. Web App, Salesforce, etc.). Available on instances running admin v25-nov or later. |
- A SearchUnify account with:
- A valid instance URL
- Authentication credentials (API Key or OAuth 2.0 credentials)
- A Search Client UID (found in SearchUnify admin panel under Search Clients, or use the
get-search-clientstool to list all available UIDs) - API scopes enabled (set scope to "All" for full access, or enable specific scopes for search, analytics, and content operations)
- Docker installed (for Docker-based deployment)
- An MCP-compatible client (e.g. Claude Desktop, Cursor)
The server supports three authentication methods:
The simplest method. Requires an API key generated from your SearchUnify instance.
Requires username, password, client ID, and client secret. Best for user-specific access.
Requires client ID and client secret only. Best for service-to-service access without a specific user context.
The server supports two transport mechanisms and can run them simultaneously.
| Environment Variable | Values | Default | Description |
|---|---|---|---|
MCP_TRANSPORT |
stdio, http, both |
both |
Which transport(s) to start. |
MCP_HTTP_PORT |
number | 3000 |
Port for the HTTP transport (when mode is http or both). |
- Used by local MCP clients such as Claude Desktop.
- Communicates over stdin/stdout.
- No additional configuration needed.
- Used by remote or cloud-based MCP clients.
- Exposes a Streamable HTTP endpoint at
http://localhost:<MCP_HTTP_PORT>. - Clients send JSON-RPC requests via POST; SSE streaming is supported via GET.
- Runs in stateless mode so multiple clients can connect and initialize concurrently.
- Credentials can be provided per-request via HTTP headers (see below) or fall back to
creds.json.
Set MCP_TRANSPORT=both to serve stdio and HTTP simultaneously on the same process.
# HTTP only on port 4000
MCP_TRANSPORT=http MCP_HTTP_PORT=4000 node src/index.js
# Both stdio and HTTP on default port 3000
MCP_TRANSPORT=both node src/index.jsThis is the traditional local setup using Docker and a credentials file.
Choose one of the following formats based on your authentication method:
API Key authentication:
{
"instance": "<searchunify_instance_url>",
"timeout": 60000,
"authType": "apiKey",
"apiKey": "<your_api_key>",
"uid": "<search_client_uid>"
}Password authentication:
{
"instance": "<searchunify_instance_url>",
"timeout": 60000,
"authType": "password",
"oauth2": {
"username": "<your_email>",
"password": "<your_password>",
"clientId": "<your_client_id>",
"clientSecret": "<your_client_secret>"
},
"uid": "<search_client_uid>"
}Client Credentials authentication:
{
"instance": "<searchunify_instance_url>",
"timeout": 60000,
"authType": "clientCredentials",
"oauth2": {
"clientId": "<your_client_id>",
"clientSecret": "<your_client_secret>"
},
"uid": "<search_client_uid>"
}Locate the Claude Desktop configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add the following entry to the mcpServers section:
{
"mcpServers": {
"su-mcp": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-v",
"<path_to_creds.json>:/app/src/input/creds.json:ro",
"searchunifyutils/su-mcp"
]
}
}
}Replace <path_to_creds.json> with the absolute path to your creds.json file.
Docker Image Tags: Use
searchunifyutils/su-mcp(defaults tolatest) or pin a specific version withsearchunifyutils/su-mcp:2.0.0. Available tags are published on Docker Hub.
Fully quit (Cmd+Q on macOS) and reopen Claude Desktop to apply the updated configuration.
Run the MCP server directly with Node.js without Docker. Requires Node.js 18+ (Node 20+ recommended) and a creds.json file.
git clone https://github.com/searchunify/su-mcp.git
cd su-mcp
npm installCreate src/input/creds.json with your credentials (see Integration 1, Step 1 for format options).
{
"mcpServers": {
"su-mcp": {
"command": "node",
"args": [
"/absolute/path/to/su-mcp/src/index.js"
],
"env": {
"MCP_TRANSPORT": "stdio"
}
}
}
}Replace /absolute/path/to/su-mcp with the actual path where you cloned the repository.
Note: Setting
MCP_TRANSPORTtostdiois recommended for local Claude Desktop usage. If omitted, the default isboth, which also starts an HTTP server on port 3000.
Fully quit (Cmd+Q on macOS) and reopen Claude Desktop to apply the updated configuration.
For connecting to a SearchUnify MCP server over HTTP, use mcp-remote. Credentials are passed as HTTP headers on every request — no local creds.json is needed. To run su-mcp via mcp-remote node version 24 is required on the machine.
Endpoint note: Use the root URL (
https://mcp.searchunify.com/) — not/mcp. On OAuth-enabled servers the/mcpendpoint requires a Bearer token; the root endpoint (/) always accepts header-based credentials for backward compatibility.
{
"mcpServers": {
"su-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.searchunify.com/",
"--header",
"searchunify-instance:<searchunify_instance_url>",
"--header",
"searchunify-timeout:60000",
"--header",
"searchunify-auth-type:<apiKey|password|clientCredentials>",
"--header",
"searchunify-api-key:<your_api_key>",
"--header",
"searchunify-uid:<search_client_uid>"
]
}
}
}All header names use the searchunify- prefix (lowercase):
| Header | Required | Description |
|---|---|---|
searchunify-instance |
Yes | Your SearchUnify instance URL (e.g. https://your-instance.searchunify.com). |
searchunify-uid |
Yes | Search Client UID. |
searchunify-auth-type |
Yes | Authentication method: apiKey, password, or clientCredentials. |
searchunify-timeout |
No | Request timeout in milliseconds. Defaults to 60000. |
Additional headers by auth type:
| Auth Type | Additional Headers |
|---|---|
apiKey |
searchunify-api-key -- Your API key. |
password |
searchunify-oauth-username, searchunify-oauth-password, searchunify-oauth-client-id, searchunify-oauth-client-secret |
clientCredentials |
searchunify-oauth-client-id, searchunify-oauth-client-secret |
Note: When HTTP headers are present and valid, they take priority over any
creds.jsonfile on the server. If headers are missing or incomplete, the server falls back tocreds.json.
{
"mcpServers": {
"su-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.searchunify.com/",
"--header",
"searchunify-instance:https://your-instance.searchunify.com",
"--header",
"searchunify-timeout:60000",
"--header",
"searchunify-auth-type:apiKey",
"--header",
"searchunify-api-key:<your_api_key>",
"--header",
"searchunify-uid:<search_client_uid>"
]
}
}
}{
"mcpServers": {
"su-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.searchunify.com/",
"--header",
"searchunify-instance:https://your-instance.searchunify.com",
"--header",
"searchunify-timeout:60000",
"--header",
"searchunify-auth-type:password",
"--header",
"searchunify-oauth-username:<your_email>",
"--header",
"searchunify-oauth-password:<your_password>",
"--header",
"searchunify-oauth-client-id:<your_client_id>",
"--header",
"searchunify-oauth-client-secret:<your_client_secret>",
"--header",
"searchunify-uid:<search_client_uid>"
]
}
}
}{
"mcpServers": {
"su-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.searchunify.com/",
"--header",
"searchunify-instance:https://your-instance.searchunify.com",
"--header",
"searchunify-timeout:60000",
"--header",
"searchunify-auth-type:clientCredentials",
"--header",
"searchunify-oauth-client-id:<your_client_id>",
"--header",
"searchunify-oauth-client-secret:<your_client_secret>",
"--header",
"searchunify-uid:<search_client_uid>"
]
}
}
}When installed from Claude's public directory, authentication is handled via OAuth 2.0 using a proxy flow — the MCP server delegates login to your SearchUnify instance. No API keys or passwords are stored in Claude.
OAuth proxy flow:
Claude → MCP /authorize (form) → SU /auth/authorise_redirect (SU login) → MCP /su-callback → Bearer token
Before connecting, an OAuth client must be registered in your SearchUnify instance with the MCP server's callback URL as the redirect_uri.
- Log in to your SearchUnify Admin panel
- Navigate to OAuth Clients (usually under Settings or Developer)
- Create a new OAuth client with:
- Name: e.g.
Claude MCP Connector - Redirect URI:
<MCP_ISSUER_URL>/su-callback
e.g.https://mcp.searchunify.com/su-callback - Grant Types:
authorization_code - Scope:
All(or the scopes required for search and analytics)
- Name: e.g.
- Note the generated Client ID and Client Secret — users will need these when connecting
Set these environment variables before starting the MCP server:
| Variable | Required | Description |
|---|---|---|
OAUTH_ENCRYPTION_KEY |
Yes | 64-character hex string for AES-256 encryption of stored tokens |
MCP_ISSUER_URL |
Yes | Public HTTPS URL of your MCP server (e.g., https://mcp.searchunify.com) |
REDIS_URL |
No | Redis connection URL (e.g., redis://localhost:6379). Omit to use in-memory store (tokens lost on server restart). |
Generate an encryption key:
openssl rand -hex 32Security: Never commit
OAUTH_ENCRYPTION_KEYorREDIS_URLto version control. Use environment variables, Docker secrets, or a secrets manager.
When a user connects via Claude's directory:
- Claude opens the SearchUnify connection form
- The user enters:
- SearchUnify Instance URL — e.g.
https://your-instance.searchunify.com - Search Client UID — found in SearchUnify Admin → Search Clients
- OAuth Client ID — from the client registered in Step 1
- OAuth Client Secret — from the client registered in Step 1
- SearchUnify Instance URL — e.g.
- The user is redirected to their SearchUnify login page
- After login, an MCP Bearer token is issued and all tools are available in Claude
Use this integration when the standard OAuth flow (Integration 4) does not work — for example when Claude Desktop fails to auto-complete the browser redirect, or when you prefer to initiate login manually from within the chat.
A browser is required — the difference from Integration 4 is that the browser is opened manually by clicking a link surfaced in the chat, rather than being opened automatically by mcp-remote.
How it works:
Claude Desktop → connects to /mcp-connect
→ Claude calls login() tool automatically
→ Chat shows: [Connect SearchUnify](https://mcp.searchunify.com/mcp-connect/login?s=...)
→ User clicks the link → browser opens the connection form
→ User fills in: Instance URL, Search Client UID, OAuth Client ID, OAuth Client Secret
→ User is redirected to their SearchUnify login page (SSO supported)
→ After login → "Login Successful" page in browser
→ User returns to Claude → all tools are now available
- The MCP server must have
OAUTH_ENCRYPTION_KEYandMCP_ISSUER_URLset (same as Integration 4). - An OAuth client must be registered in your SearchUnify Admin with
<MCP_ISSUER_URL>/su-callbackas an allowed redirect URI (e.g.https://mcp.searchunify.com/su-callback).
Note: If the SU OAuth client does not have the MCP server's
/su-callbackURL registered, SU will redirect to its own dashboard after login instead of completing the connection. Always register the exact callback URL for each environment (including localhost for local testing).
{
"mcpServers": {
"su-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://mcp.searchunify.com/mcp-connect"
]
}
}
}On first use, Claude will automatically call the login tool and display the connection link. Click it, complete the form in your browser, log in, and return to Claude. All tools are then available for the duration of the session (1 hour).
If the session expires, call the login tool again to reconnect.
Here are some example prompts you can use with Claude after connecting SearchUnify:
Search for content:
"Search for 'how to configure SSO' in SearchUnify"
Claude will use the search tool to query your SearchUnify instance and return relevant results with titles, summaries, and links.
Explore filters before searching:
"What filter options are available for the query 'API documentation'?"
Claude will use get-filter-options to discover available facets (e.g., by source, category, content type), then you can refine your search.
Get analytics reports:
"Show me the top 10 search queries with no clicks from the last 30 days"
Claude will use the analytics tool with reportType: searchQueryWithNoClicks to retrieve queries that returned results but received no user clicks.
List search clients:
"List all search clients configured in my SearchUnify instance"
Claude will use get-search-clients to show all available search clients with their names and UIDs.
A test client script is provided at scripts/test-mcp-client.js to verify the server works correctly.
Test stdio (spawns the server process; requires src/input/creds.json):
npm run test:stdio
# or
node scripts/test-mcp-client.js stdioTest HTTP (server must already be running in another terminal):
# Terminal 1 -- Start the server
MCP_TRANSPORT=http node src/index.js
# Terminal 2 -- Run the test client
npm run test:http
# or
node scripts/test-mcp-client.js httpUse MCP_HTTP_URL to connect to a different URL:
MCP_HTTP_URL=http://localhost:4000 node scripts/test-mcp-client.js httpThe test client connects, lists all available tools, pings the server, and optionally calls the search and get-filter-options tools with sample queries.
Test OAuth flow (server must be running with OAUTH_ENCRYPTION_KEY and MCP_ISSUER_URL set):
# Opens browser — fill the form and log in to complete the flow
npm run test:oauth:visual
# With pre-filled credentials (set env vars to skip manual entry):
SU_INSTANCE=https://your-instance.searchunify.com \
SU_UID=<search_client_uid> \
SU_AUTH_TYPE=clientCredentials \
SU_CLIENT_ID=<oauth_client_id> \
SU_CLIENT_SECRET=<oauth_client_secret> \
npm run test:oauth:visualUnit tests (no credentials needed):
npm testRuns validation and module import tests for all tools.
su-mcp/
├── Dockerfile # Docker image definition (Node 24 Alpine, default transport: http)
├── docker-compose.yml # Compose file for Docker-based deployment
├── .env.example # Environment variable template (copy to .env and fill in values)
├── package.json # Dependencies and scripts
├── test/
│ ├── test-new-tools.js # Unit tests for tools
│ ├── store.test.js # MemoryStore / RedisStore unit tests
│ ├── store-fallback.test.js # Store factory fallback tests
│ ├── oauth-provider.test.js # OAuth provider unit tests
│ └── validate-authorize-body.test.js # Form validation tests
├── scripts/
│ ├── test-mcp-client.js # Test client for stdio and HTTP
│ └── test-oauth-flow.js # OAuth flow test script
└── src/
├── index.js # Entry point -- transport setup (stdio/HTTP/OAuth)
├── tools.js # Tool initialization orchestrator
├── utils.js # Response formatting utilities
├── validations.js # Credential validation, header parsing, OAuth token extraction
├── input/
│ └── creds.json # Credentials file (user-provided, not in repo)
├── auth/
│ ├── oauth-provider.js # MCP OAuth proxy provider (PKCE, SU delegation, token management)
│ ├── store.js # Redis/MemoryStore with AES-256 encrypted token storage
│ └── config-form.js # HTML configuration form (instance URL, UID, SU OAuth credentials)
└── su-core/
├── index.js # Core tools initializer
├── su-core-search.js # search and get-filter-options tools
├── su-core-analytics.js # analytics tool
└── su-core-search-clients.js # get-search-clients tool
| Issue | Solution |
|---|---|
| Docker Not Found | Ensure Docker is installed and added to your system's PATH. |
| Invalid Credentials | Double-check values in creds.json or HTTP headers. |
| Missing API Scopes | Ensure the client and user have the required search and analytics scopes enabled in your SearchUnify instance. |
| Connection Refused (HTTP) | Verify the server is running with MCP_TRANSPORT=http and the port matches. |
| mcp-remote not found | Run npx mcp-remote (it will be fetched automatically) or install it globally with npm install -g mcp-remote. |
- Full OAuth 2.0 proxy flow with PKCE for Claude public directory listing (
/mcpendpoint) - Tool-based login flow (
/mcp-connect) — user clicks a link in chat to authenticate via browser - AES-256-GCM encrypted token storage with Redis (optional) or in-memory fallback
- Dynamic client registration and auto-re-registration on unknown clients
- Refresh token rotation with 24-day TTL
- Rate limiting on auth and MCP endpoints
- Security hardening: CSP headers, HSTS, PKCE validation, bearer token verification
- OAuth configuration form with inline validation, SearchUnify branding, and setup docs link
- Tool call logging with caller IP, endpoint, SU instance URL, and OAuth client ID
- Multi-platform Docker image (
linux/amd64,linux/arm64) - Server version now read from
package.json(shows correctly in all transport modes)
- Added
/mcp-connectendpoint — tool-based login for environments where Claude Desktop cannot auto-open a browser - Added
loginMCP tool (only on/mcp-connect): returns a link to the connection form in chat; user opens it manually, logs in via SU (SSO supported), and receives a "Login Successful" page - SU tokens stored by MCP session ID after tool-based login; all other tools work normally post-auth
- Sessions persist for 1 hour; expired sessions prompt the user to call
loginagain - Existing OAuth flow (
/6565/mcp,/7777/mcp) and all other integrations are unchanged
- Added OAuth 2.0 proxy flow for Claude public directory listing (PKCE, Dynamic Client Registration)
- OAuth delegates authentication to the SU instance login page — no passwords stored in MCP server
- Added
uid(Search Client UID) to OAuth form and token chain so all tools work post-auth - Added Redis/MemoryStore with AES-256-GCM encryption for tokens; falls back to in-memory if no
REDIS_URL - Added
setTimeoutoverflow fix for 30-day refresh token TTL in MemoryStore - Added configuration form (instance URL, UID, SU OAuth Client ID/Secret)
- Fixed SDK 1.28.0 compatibility:
requireBearerAuthuses{ verifier },handleRequesttakes(req, res, req.body), freshMcpServer+StreamableHTTPServerTransportper request - Upgraded
@modelcontextprotocol/sdkto1.28.0 - Added
expressandioredisdependencies - OAuth is optional — existing stdio and HTTP header auth continue to work unchanged
- Added OAuth full-flow test script:
npm run test:oauth:visual
- Added
get-search-clientstool — list all search clients configured in the instance - Added
averageClickPositionreport type to analytics tool - Added
sessionDetailsandsessionListTablereport types to analytics tool - Added
tileDataContent,tileDataMetrics1,tileDataMetrics2tile report types - Added
sessionId,pageNumber,startIndex,sortByField,sortTypeparameters to analytics tool - Added local Node.js integration guide (Integration 2)
- Updated
su-sdkdependency to2.1.0 - Improved
formatForClaudeto handle object and nested data responses
- Initial release
searchtool with pagination, sorting, and facet filteringget-filter-optionstool for discovering available filtersanalyticstool with 5 report types (getAllSearchQuery, searchQueryWithResult, searchQueryWithNoClicks, searchQueryWithoutResults, getAllSearchConversion)- Docker and mcp-remote integration support
- API Key, OAuth 2.0 Password Grant, and Client Credentials authentication
- Dual transport: stdio and Streamable HTTP
This project is subject to the SearchUnify Privacy Policy.
- Issues: GitHub Issues
- Documentation: SearchUnify Docs
- Website: searchunify.com
This project is licensed under the BSD 2-Clause License. See the LICENSE file for details.
For more information, visit SearchUnify or check out the su-sdk-js documentation.