MCP server
Connect an AI assistant to the corpus over the Model Context Protocol. Configuration for every major client.
The Commonplace exposes its corpus over the Model Context Protocol, so an AI assistant can search papers, filter graded claims and pull a full paper record without you leaving the conversation. Access is read-only. Nothing an assistant does through this endpoint can change the corpus.
The endpoint
- URL
https://commonplace.workforcefutures.net/mcp- Transport
- Streamable HTTP.
POSTonly; responses areapplication/json. - Authentication
Authorization: Bearer YOUR_API_KEY- Protocol revisions
-
2024-11-05,2025-03-26,2025-06-18,2025-11-25. The server echoes back whichever of these your client asks for. - Tools
- 8 read-only tools. See the tool reference.
Getting a key. Keys are issued by hand. Contact Alex via workforcefutures.net and say roughly what you want to use it for. Treat the key as a personal credential: it identifies you to the rate limiter, so a key you share is a budget you share.
Check it works before wiring up a client
Every client failure looks the same from inside an editor, so it is worth one
curl first. This asks the server what tools it has:
curl -sS https://commonplace.workforcefutures.net/mcp \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
A working key returns a JSON object with a result.tools array. A
bad or missing key returns 401 with
{"error":"unauthorized"}. If that works and your editor still does
not connect, the problem is the client configuration, not the server or the key.
Client configuration
Configuration below is grouped by what the client actually supports. Anything
that speaks Streamable HTTP natively should use the direct form. Clients that
only launch local processes need the mcp-remote bridge.
Claude Code
Add it from the command line:
claude mcp add --transport http commonplace \
https://commonplace.workforcefutures.net/mcp \
--header "Authorization: Bearer YOUR_API_KEY"
Or commit a .mcp.json at the project root so the whole team picks
it up:
{
"mcpServers": {
"commonplace": {
"type": "http",
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
The type field matters. An entry with a url but no
type is read as a local stdio server and will fail to start.
VS Code and GitHub Copilot
In .vscode/mcp.json for one workspace:
{
"servers": {
"commonplace": {
"type": "http",
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
To keep the key out of the file, declare it as a prompted input. VS Code asks once and stores it for you:
{
"inputs": [
{
"type": "promptString",
"id": "commonplace-key",
"description": "Commonplace API key",
"password": true
}
],
"servers": {
"commonplace": {
"type": "http",
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer ${input:commonplace-key}" }
}
}
}
Cursor
.cursor/mcp.json in a project, or ~/.cursor/mcp.json
globally:
{
"mcpServers": {
"commonplace": {
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Windsurf
In ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"commonplace": {
"serverUrl": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Zed
Under context_servers in settings.json:
{
"context_servers": {
"commonplace": {
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Claude Desktop
Claude Desktop's config file describes local processes, so a remote server
goes through the mcp-remote bridge. Edit
claude_desktop_config.json
(%APPDATA%\Claude\ on Windows,
~/Library/Application Support/Claude/ on macOS):
{
"mcpServers": {
"commonplace": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://commonplace.workforcefutures.net/mcp",
"--header",
"Authorization: Bearer YOUR_API_KEY"
]
}
}
}
Keep Authorization: Bearer YOUR_API_KEY as a single array element,
exactly as shown. Splitting it across two elements, or dropping the space after
the colon, produces a header the server will reject.
On npx. That configuration downloads and runs the
mcp-remote package from npm every time the client starts, with
whatever version is current. That is a real supply-chain dependency, not a
detail. If that is not acceptable to you, install a pinned version yourself and
point command at the installed binary, or use a client that speaks
HTTP directly and skip the bridge.
OpenAI Agents SDK (Python)
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(
name="commonplace",
params={
"url": "https://commonplace.workforcefutures.net/mcp",
"headers": {"Authorization": f"Bearer {os.environ['COMMONPLACE_API_KEY']}"},
},
) as server:
agent = Agent(
name="Research assistant",
instructions="Use the Commonplace tools for AI-economics evidence.",
mcp_servers=[server],
)
result = await Runner.run(agent, "What does the evidence say about AI and wages?")
print(result.final_output)
OpenAI Responses API
import os
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-4o",
tools=[{
"type": "mcp",
"server_label": "commonplace",
"server_url": "https://commonplace.workforcefutures.net/mcp",
"headers": {"Authorization": f"Bearer {os.environ['COMMONPLACE_API_KEY']}"},
"require_approval": "never",
}],
input="Summarise the strongest evidence on AI and developer productivity.",
)
print(response.output_text)
MCP Python SDK
import asyncio, os, httpx2
from mcp import Client
from mcp.client.streamable_http import streamable_http_client
async def main():
async with httpx2.AsyncClient(
headers={"Authorization": f"Bearer {os.environ['COMMONPLACE_API_KEY']}"},
timeout=httpx2.Timeout(30.0, read=120.0),
) as http_client:
transport = streamable_http_client(
"https://commonplace.workforcefutures.net/mcp",
http_client=http_client,
)
async with Client(transport) as client:
tools = await client.list_tools()
print([t.name for t in tools.tools])
result = await client.call_tool("search_papers", {"query": "wages", "limit": 5})
print(result.content[0].text)
asyncio.run(main())
Headers go on the httpx2 client, not on
streamable_http_client, which does not take a
headers argument.
MCP Inspector
Useful for confirming a key works and for reading the raw tool schemas:
npx @modelcontextprotocol/inspector --cli \
https://commonplace.workforcefutures.net/mcp \
--transport http \
--header "Authorization: Bearer YOUR_API_KEY" \
--method tools/list
If the key is wrong, Inspector does not simply report the 401. It
tries to start an interactive OAuth flow and fails with a message about needing
a TTY. That means the key was rejected, not that OAuth is required.
Rate limits
The endpoint is metered per key, in three tiers, because one of the tools costs real money on every call:
| Class | What it covers | Budget |
|---|---|---|
| Control | initialize, ping, tools/list, notifications |
240/minute |
| Tools | Ordinary tool calls that only read the local corpus | 60/minute |
| Billable | semantic_search, which computes an embedding per call |
10/minute, and a daily cap |
Over budget returns HTTP 429 with a Retry-After
header. Normal interactive use does not come close to these numbers; a runaway
loop does. If you have a legitimate bulk use case, ask rather than working
around it.
When it does not connect
401 unauthorized-
The key is missing, wrong, or the header is malformed. The value must be
Bearer, one space, then the key. Confirm with thecurlabove. 405 method not allowed-
Something sent
GET. This server is POST-only and has no server-initiated event stream. A client that requires aGET/SSE channel cannot use it directly; bridge it withmcp-remote. 429 rate limited-
You are over one of the budgets above. Wait for
Retry-After. If this happens during ordinary use, tell us, because the budget is wrong. 400 unsupported protocol version-
Your client sent an
MCP-Protocol-Versionthe server does not implement. The supported list is in the error body. - The client starts but shows no tools
-
Usually a config entry missing its
type, so the client is trying to run it as a local command. Check the client's MCP log.
Vendor configuration formats change. If a snippet here does not match what your client expects, trust the client's own documentation and tell us so this page can be corrected.