Tools API

You host the MCP server. Miso does the searching.

This page is for a team that wants to run its own MCP server, and wants Miso's agentic search inside it. You keep the server, the transport, the tool names and the authentication. Miso answers the two calls underneath: one that lists the tools, one that runs a search and returns passages worth citing.

What the search does: a question arrives in a reader's own words, and the answer comes back as ranked passages from your archive, each with its title, date and URL to cite.

Below we make both calls, then put them inside a working MCP server.

Prefer Miso to run the server too? Your MCP Host gives you an address on your own domain, with an install page and sign-in built in.


The two calls

Call What it gives you
GET /v1/ask/mcp/tools Every tool your app exposes, with a JSON Schema for its arguments.
POST /v1/ask/mcp/tools/{name} The result of running one tool.

Both live on https://api.askmiso.com. Both take your Secret API Key, in either header:

X-API-Key: YOUR_SECRET_API_KEY
Authorization: Bearer YOUR_SECRET_API_KEY

Without a key the reply is 401. The key is a server credential, so these calls belong on your back end.


List the tools

curl -s https://api.askmiso.com/v1/ask/mcp/tools \
  -H "X-API-Key: YOUR_SECRET_API_KEY"
{
  "tools": [
    {
      "name": "ask_your_site",
      "description": "Search the Your Site archive — news, analysis and reference.",
      "input_schema": {
        "type": "object",
        "properties": { "question": { "type": "string" } },
        "required": ["question"]
      }
    },
    {
      "name": "get_your_site_article",
      "description": "Get the full text of a specific snippet returned by ask_your_site.",
      "input_schema": {
        "type": "object",
        "properties": {
          "product_id": { "type": "string" },
          "offset": { "type": "integer" }
        },
        "required": ["product_id", "offset"]
      }
    }
  ],
  "version": "1.0.0"
}

Two tools come back. A search tool, named for your brand, and a detail tool that returns the full text of one result. The names and descriptions are the ones Miso set up with you, so ask_your_site above stands for your own tool name.

input_schema is JSON Schema. Hand it to your MCP server as the tool's schema rather than retyping it, and a change Miso makes reaches your agent on your next start.


Run a tool

Arguments go in an arguments object:

curl -s https://api.askmiso.com/v1/ask/mcp/tools/ask_your_site \
  -H "X-API-Key: YOUR_SECRET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"question": "What happened at the 2012 Olympics opening ceremony?"}}'
{
  "success": true,
  "result": "{\"query\": \"...\", \"results\": [ ... ], \"citation_format\": \"...\"}"
}

result is a string. It holds JSON, so parse it once more before you read it. Returning it as a string is what an MCP client expects to receive, so a server that passes it straight through needs no parsing at all.

Inside, a search result carries the fields your agent cites from:

Field What it is
product_id The article. Pass it back to the detail tool.
offset Which passage of that article matched. The detail tool needs it too.
product_title The headline, for the citation.
url Where the citation points.
published_date The date, for recency questions.
snippet_preview The passage that matched, to show or to judge relevance.
relevance_score How well it matched, between 0 and 1.

The detail tool takes product_id and offset from a result and returns the same fields plus content, the full text.


Wrap them in your server

The whole integration is one function that forwards a call, plus one tool declaration for each Miso tool. In the following server, we expose both tools under our own names:

import os, requests
from mcp.server.mcpserver import MCPServer

MISO_BASE = "https://api.askmiso.com/v1/ask/mcp/tools"
HEADERS = {"X-API-Key": os.environ["MISO_SECRET_API_KEY"]}

mcp = MCPServer("your-site")


def call_miso(name, arguments):
    response = requests.post(
        f"{MISO_BASE}/{name}", headers=HEADERS, json={"arguments": arguments}, timeout=60
    )
    response.raise_for_status()
    body = response.json()
    if not body["success"]:
        raise RuntimeError(body["error"])
    return body["result"]


@mcp.tool()
def search(question: str) -> str:
    """Search our archive. Returns snippets with titles, dates and URLs."""
    return call_miso("ask_your_site", {"question": question})


@mcp.tool()
def get_article(product_id: str, offset: int) -> str:
    """Return the full text of one result from search."""
    return call_miso("get_your_site_article", {"product_id": product_id, "offset": offset})


if __name__ == "__main__":
    mcp.run()

Your agent now sees search and get_article on your server. The tools keep Miso's behaviour, and the name, the description and the transport are yours.

Your key stays on your server, and your subscribers never hold a Miso credential. Access is yours to control: put your own API key or your own sign-in in front of this server, and it decides who gets in.


Narrow a connection to content types

Add type= to either call, and the tools answer only from those content types. The value is a comma-separated list, matched exactly as your content is indexed:

curl -s "https://api.askmiso.com/v1/ask/mcp/tools?type=COURSE,SERVICE" \
  -H "X-API-Key: YOUR_SECRET_API_KEY"

Send the same parameter on the call that runs a tool. A listing narrowed this way also says so in the tool description, so the agent knows what it is searching. This is how one app serves two servers: a wide one for your own team, and a narrow one for a customer-facing agent.


When something fails

What you get What happened
401 No key, or a key that is not this app's secret key.
404 with Tool not found The tool name is not one from the listing.
200 with "success": false The tool ran and failed. error says why.

The third one is the one to handle deliberately. A missing required argument arrives here, with success: false and a message naming the argument. Raise it as a tool error in your server so the agent can correct itself and try again.


Next