Use it from code.
The server at https://thinkhard.si/mcp speaks plain JSON over HTTPS, so you can call it with curl, from a script, with an MCP SDK, or hand it to Claude or GPT through their APIs and let the model call it. No key, no sign-up.
Plain HTTP
Every call is one POST of a JSON-RPC 2.0 message. The server keeps no session, so there is nothing to open first: send tools/call straight away. Ask for JSON in the Accept header, as the MCP spec says; the answer is always application/json, never a stream.
curl -s https://thinkhard.si/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"grade_prompt","arguments":{"prompt":"Summarize these meeting notes."}}}'
The answer carries the result twice: as text for a model to read, and as data for your code.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "Score: 1 out of 10 (Needs work), read as a prompt for Any chat model.\n\n[pass] What to make: ..." }],
"structuredContent": {
"score": 1,
"band": "Needs work",
"target": "any",
"checks": [{ "check": "What to make", "pass": true }, { "check": "Who it's for", "pass": false }, ...]
}
}
}
To see what the server offers, send tools/list or prompts/list the same way. The tools reference lists every tool's parameters and fields, and the playground builds any of these calls for you and shows the answer.
JavaScript
Works in Node 18 or later, Deno, Bun and the browser. The server sends CORS headers, so a web page on any domain can call it.
const res = await fetch("https://thinkhard.si/mcp", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: { name: "search_prompts", arguments: { query: "cover letter", limit: 3 } },
}),
});
const { result, error } = await res.json();
if (error) throw new Error(error.message);
for (const p of result.structuredContent.prompts) console.log(p.id, p.title, p.url);
Python
With requests (pip install requests):
import requests
res = requests.post(
"https://thinkhard.si/mcp",
headers={"Accept": "application/json, text/event-stream"},
json={
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {"name": "search_prompts", "arguments": {"query": "cover letter", "limit": 3}},
},
)
answer = res.json()
for p in answer["result"]["structuredContent"]["prompts"]:
print(p["id"], p["title"])
The official MCP SDKs
For an app that talks to several MCP servers, the official SDKs handle the protocol for you. These run as written against the live server.
TypeScript
npm install @modelcontextprotocol/sdk (tested with 1.32):
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const client = new Client({ name: "my-app", version: "1.0.0" });
await client.connect(new StreamableHTTPClientTransport(new URL("https://thinkhard.si/mcp")));
const { tools } = await client.listTools();
console.log(tools.map((t) => t.name));
const result = await client.callTool({ name: "grade_prompt", arguments: { prompt: "Summarize these meeting notes." } });
console.log(result.structuredContent.score, result.content[0].text);
await client.close();
Python
pip install "mcp>=2" (tested with 2.3). In version 1 of the SDK the function was streamablehttp_client, it gave three values instead of two, and the result field was structuredContent.
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main():
async with streamable_http_client("https://thinkhard.si/mcp") as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("grade_prompt", {"prompt": "Summarize these meeting notes."})
print(result.structured_content["score"])
print(result.content[0].text)
asyncio.run(main())
Claude API: let Claude call it
With the MCP connector in the Messages API, Claude calls the server itself in the middle of a reply: it can find a prompt in the repository, fill it in and answer with it. Name the server in mcp_servers and turn on its tools with an mcp_toolset entry. No authorization token is needed.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5-5",
"max_tokens": 2048,
"mcp_servers": [{ "type": "url", "url": "https://thinkhard.si/mcp", "name": "thinkhard" }],
"tools": [{ "type": "mcp_toolset", "mcp_server_name": "thinkhard" }],
"messages": [{ "role": "user", "content": "Grade this prompt with thinkhard, then rewrite it to fix what is missing: Write a cover letter for a marketing job." }]
}'
The same with the Python SDK (pip install anthropic):
import anthropic
client = anthropic.Anthropic()
message = client.beta.messages.create(
model="claude-sonnet-5-5",
max_tokens=2048,
betas=["mcp-client-2025-11-20"],
mcp_servers=[{"type": "url", "url": "https://thinkhard.si/mcp", "name": "thinkhard"}],
tools=[{"type": "mcp_toolset", "mcp_server_name": "thinkhard"}],
messages=[{"role": "user", "content": "Find a thinkhard prompt for turning meeting notes into action items."}],
)
print("".join(block.text for block in message.content if block.type == "text"))
The connector uses MCP tools only, not MCP prompts, so Claude reaches the templates through search_prompts and get_prompt. To offer only some tools, set "default_config": {"enabled": false} on the toolset and turn tools on one by one in configs. See Anthropic’s MCP connector documentation for the details.
OpenAI Responses API
The Responses API takes a remote MCP server as a tool. Every thinkhard.si tool is read-only, so you can skip the approval step with "require_approval": "never". Like the Claude API, it uses MCP tools only, not MCP prompts.
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-astra",
"tools": [{
"type": "mcp",
"server_label": "thinkhard",
"server_url": "https://thinkhard.si/mcp",
"require_approval": "never"
}],
"input": "What would 20,000 support replies a month cost on the cheapest models? Use thinkhard."
}'
The same with the Python SDK (pip install openai):
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[{
"type": "mcp",
"server_label": "thinkhard",
"server_url": "https://thinkhard.si/mcp",
"require_approval": "never",
"allowed_tools": ["search_prompts", "get_prompt"],
}],
input="Find a thinkhard prompt for a performance self-review and show me its blanks.",
)
print(response.output_text)
Limits, versions and changes
- Rate limit. 600 calls an hour from one network. Past that, the server answers HTTP 429 with a
Retry-Afterheader. If you need more, write to contact@thinkhard.si. - Protocol. MCP 2025-11-25, and the three versions before it (2025-06-18, 2025-03-26, 2024-11-05). The server answers
initializewith the version you ask for, or the newest if it does not know yours. - Versions. The server is at version 1.1.0, shown in
serverInfo. Changes are additive: new tools, new optional parameters and new fields. A tool or field is never renamed or removed without a note in the changelog first. - Errors. Listed in the tools reference. A tool that cannot do what was asked answers normally with
isError: true. - Discovery. The server is in the official MCP Registry as
si.thinkhard/prompt-tools, and this whole reference is one Markdown file at /developers/mcp.md, for coding assistants.
Built something with it? Tell us at contact@thinkhard.si. We read every message.