Link Search Menu Expand Document Documentation Menu

MCP Streamable HTTP Server API

Introduced 3.3

The MCP server is exposed via the /_plugins/_ml/mcp endpoint and implements the Streamable HTTP transport defined by the Model Context Protocol (MCP). It allows agents or clients to connect to OpenSearch and discover or invoke available tools.

This server does not open a persistent SSE connection with the client; all communication happens over stateless HTTP calls. If a client sends a GET request (typically, to establish an SSE connection), the server returns a 405 Method Not Allowed response, allowing the client to continue using POST communication.

To learn more about the transport, see the official MCP documentation.

Prerequisites

Before you can connect to the MCP server endpoint, you need to enable the MCP server functionality in your cluster:

PUT /_cluster/settings
{
  "persistent": {
    "plugins.ml_commons.mcp_server_enabled": "true"
  }
}

Optionally, you can register tools so clients can discover and call them. See Register MCP tools.

Connecting to the MCP server

You can connect to the MCP server using any client that supports the Streamable HTTP transport.

Connecting using an MCP client

The following example uses fastmcp to initialize a connection, list tools, and call a tool:

import asyncio, logging
from fastmcp import Client


async def main():
    async with Client("http://localhost:9200/_plugins/_ml/mcp") as client:
        for t in await client.list_tools():
            print(t.name)
        r = await client.call_tool("ListIndexTool", {})
        print("result: ", r)

asyncio.run(main())

Invoking the MCP server manually (for debugging)

While not required for normal usage, you can manually invoke the MCP server using JSON-RPC calls over HTTP. The following example presents typical MCP client behavior.

Step 1: Initialize a connection

Send an initialize method with your client information and capabilities. Note that the protocolVersion must match the MCP specification version:

POST /_plugins/_ml/mcp
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {}
    },
    "clientInfo": {
      "name": "test-client",
      "version": "1.0.0"
    }
  }
}

The server responds with its capabilities and server information. The tools.listChanged indicates that the server supports dynamic tool discovery:

200 OK
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": false
      },
      "resources": {
        "subscribe": false,
        "listChanged": false
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "OpenSearch-MCP-Stateless-Server",
      "version": "0.1.0"
    },
    "instructions": "OpenSearch MCP Stateless Server - provides access to ML tools without sessions"
  }
}

Step 2: Send an initialization complete notification

Send a notification to indicate that initialization is complete. This notification does not expect a response payload:

POST /_plugins/_ml/mcp
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized",
  "params": {}
}

The server acknowledges the notification with a 202 Accepted status:

202 Accepted

Step 3: List available tools

Use the tools/list method to discover available tools:

POST /_plugins/_ml/mcp
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}

For a dedicated API, see List MCP tools.

The server returns an array of available tools with their names, descriptions, and input schemas. Notice how each tool includes a detailed inputSchema that describes the expected parameters:

200 OK
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "ListIndexTool",
        "description": "This tool returns information about indices in the OpenSearch cluster along with the index `health`, `status`, `index`, `uuid`, `pri`, `rep`, `docs.count`, `docs.deleted`, `store.size`, `pri.store. size `, `pri.store.size`, `pri.store`. Optional arguments: 1. `indices`, a comma-delimited list of one or more indices to get information from (default is an empty list meaning all indices). Use only valid index names. 2. `local`, whether to return information from the local node only instead of the cluster manager node (Default is false)",
        "inputSchema": {
          "type": "object",
          "properties": {
            "indices": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "OpenSearch index name list, separated by comma. for example: [\"index1\", \"index2\"], use empty array [] to list all indices in the cluster"
            }
          },
          "additionalProperties": false
        }
      }
    ]
  }
}

Step 4: Call a tool

Use the tools/call method to invoke a specific tool. Provide the tool name and arguments that match the tool’s input schema:

POST /_plugins/_ml/mcp
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "ListIndexTool",
    "arguments": {
      "indices": []
    }
  }
}

The server executes the tool and returns the result in the content array. The isError field indicates whether the tool execution was successful:

200 OK
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "row,health,status,index,uuid,pri(number of primary shards),rep(number of replica shards),docs.count(number of available documents),docs.deleted(number of deleted documents),store.size(store size of primary and replica shards),pri.store.size(store size of primary shards)\n1,green,open,.plugins-ml-config,nKyzDAupTGCwuybs9S_iBA,1,0,1,0,3.9kb,3.9kb\n2,green,open,.plugins-ml-mcp-tools,k1QwQKmXSeqRexmB2JDJiw,1,0,1,0,5kb,5kb\n"
      }
    ],
    "isError": false
  }
}
350 characters left

Have a question? .

Want to contribute? or .