Tools API
You host the MCP server. Miso does the searching.
Which setup?
Internal Use
Zero effort. No sign-in.
For your Subscribers
Subscribers sign in with your existing login.
MCP Proxy API
Your gateway signs users in.
Tools API (this page)
Your MCP server calls Miso tools.
Use the Tools API when you run your own MCP server and want Miso search as one of its tools. Your server keeps the transport, the tool names and the sign-in. Miso answers two HTTP calls:
GET /v1/ask/mcp/toolslists the tools.POST /v1/ask/mcp/tools/{name}runs one tool.
The search tool takes a question in the words of a reader. It returns ranked passages from your archive, and each passage has a title, a date and a URL to cite.
Below we make both calls, then put them inside a working MCP server.
How a call travels
Your server owns both sign-in and the MCP protocol. Miso sees plain HTTP calls and returns JSON.
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 get_snippet_detail. |
offset |
Which passage of that article matched. get_snippet_detail 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. |
get_snippet_detail 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.
Narrow a call with a filter
Add a filter in Lucene syntax as ?fq= on the call that runs a tool:
curl -s "https://api.askmiso.com/v1/ask/mcp/tools/ask_your_site?fq=tags:public" \
-H "X-API-Key: YOUR_SECRET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"arguments": {"question": "What changed in the 2024 rules?"}}'
The filter can only narrow. Miso adds it as one clause and keeps its own filter for your app on top, so a call never reaches content that your app excludes. Your server chooses the filter for each call. For example, it can give each user only the content that their plan allows.
Brackets must open and close within the filter, and phrases and regular
expressions must close. If a filter breaks one of these rules, ends with a
backslash, or is longer than 2,000 characters, the call is refused with 400.
No search runs. ?type= still works beside ?fq=, and the two combine.
The listing call reads ?type= only. ?fq= changes the results, and the tool
descriptions stay the same.
Tell Miso who the user is
Send your own id for the user on the call that runs a tool:
X-MCP-User-Id: user-123
You can also send it as ?user_id=. If you send both, the header wins. Miso
records the id with each search and each full-text read, so your usage report
splits by user. Use an opaque id, such as a database key, and do not use an
email address. The id can be up to 256 characters, with no control characters.
The user id is your assertion. Miso accepts it only with your Secret API Key.
With a publishable key, the call is refused with 401.
Put ?fq= on the URL and the user id in the header. If you put fq or
user_id next to arguments in the body, the call is refused with 400. The
error message tells you where the value goes.
Full text follows the same rules
get_snippet_detail returns the full text of a result only when the two calls
match. The call to get_snippet_detail must have the same user id, the same
?fq= and the same ?type= as the search that returned the result. The
search must also be less than one hour old.
If one of these is different, the call still answers "success": true, but
result holds "error": "Snippet not accessible". Search again with the same
parameters. In the server above, add the parameters in call_miso, so that
both tools send them.
When something fails
| What you get | What happened |
|---|---|
400 |
?fq= is not valid, or fq or user_id is in the body. detail says which. |
401 |
No key, a key that is not this app's secret key, or a user id with a publishable 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
- MCP — for your Subscribers — a Miso-run server on your own domain.
- MCP — Internal Use — connect a client with no server of your own.
- MCP — for your Subscribers — per-subscriber sign-in.
