tools/list
Enumerate every tool visible to the authenticated caller on the CoreSpeed MCP endpoint.
https://api.corespeed.io/mcptools/listtools/list is the authority on what this caller can see. It is filtered
per request by connected accounts, hidden registrations, and organization plus
member capability settings — never cache it across identities, and never build
a static tool inventory in application code.
Visibility is not executability. The list carries the nine session-gated
manage__* tools (keys_*, whoami, accounts_*) for an API-key caller too —
they answer jwt_session_required instead of running. It also carries tools from
connectors in needs_reauth, which fail until the account is reauthorized.
Absence proves a tool cannot be called; presence does not prove it can. Branch on
the result, not on membership in this list.
The four manage__remote_* tools (remote_add, remote_list, remote_refresh,
remote_remove) are the deliberate exception — they are not session-gated and do
execute for an API-key caller, subject to the org role check. See
tools/call.
Authorizations
AuthorizationstringheaderrequiredBearer <token>— an MCP OAuth token, ask-cs-…API key, or a user session JWT.x-api-key: <sk-cs-…>is accepted in its place.Acceptstringheaderrequiredapplication/json, text/event-stream— the Streamable HTTP transport requires both media types even though CoreSpeed answers with JSON.Content-Typestringheaderrequiredapplication/json. The transport rejects anything else with415.
Request
paramsobjectoptionalSend
{}. CoreSpeed returns the full visible surface in one response; there is no pagination cursor to follow today.
Response fields
tools[].namestringrequiredThe wire name,
<capability-id>__<operation>. Pass it verbatim totools/call.tools[].descriptionstringoptionalModel-facing description of when to use the tool. Optional in the MCP schema — CoreSpeed's own tools always set it, but a tool proxied from an org-registered remote MCP server may omit it. Do not require it.
tools[].inputSchemaobjectrequiredJSON Schema for the
argumentsobject. Validate against this rather than against examples.tools[].annotationsobjectoptionalDisplay title plus behavior hints (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint). Useful for deciding what needs a human in the loop.
A connector contributes tools as soon as the caller has a stored visible
credential for it — including an account in needs_reauth, whose tool calls
then fail until the account is reauthorized. Presence in tools/list means
visible, not necessarily healthy; check GET /connectors for account health.
curl https://api.corespeed.io/mcp \
-H "Authorization: Bearer $CORESPEED_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'const res = await fetch("https://api.corespeed.io/mcp", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CORESPEED_API_KEY}`,
"Content-Type": "application/json",
Accept: "application/json, text/event-stream",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 2,
method: "tools/list",
params: {},
}),
});
const { result } = await res.json();
for (const tool of result.tools) {
console.log(tool.name);
}import os
import httpx
res = httpx.post(
"https://api.corespeed.io/mcp",
headers={
"Authorization": f"Bearer {os.environ['CORESPEED_API_KEY']}",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
},
json={"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}},
)
for tool in res.json()["result"]["tools"]:
print(tool["name"])package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": map[string]any{},
})
req, _ := http.NewRequest("POST", "https://api.corespeed.io/mcp", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("CORESPEED_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json, text/event-stream")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var out struct {
Result struct {
Tools []struct {
Name string `json:"name"`
} `json:"tools"`
} `json:"result"`
}
json.NewDecoder(res.Body).Decode(&out)
for _, tool := range out.Result.Tools {
fmt.Println(tool.Name)
}
}{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "memory__search_memory",
"description": "Search saved long-term memories for facts relevant to the current question or task.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" }
},
"required": ["query"]
},
"annotations": {
"title": "Search memory",
"readOnlyHint": true,
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": false
}
},
{
"name": "notion__create_page",
"description": "Create a page in the connected Notion workspace.",
"inputSchema": {
"type": "object",
"properties": {
"parent_page_id": { "type": "string" },
"title": { "type": "string" }
},
"required": ["parent_page_id", "title"]
}
}
]
}
}A JSON array body is refused before any tool runs: one rate-limit slot must not carry an unbounded number of tool calls.
{
"jsonrpc": "2.0",
"id": null,
"error": {
"code": -32600,
"message": "JSON-RPC batch requests are not supported; send one request per tool call."
}
}