Get started

Quickstart

Bring up the Cortex MCP server locally, mint a bearer token, and call a tool from the client of your choice. Five minutes, two commands, no SaaS dependency.

1. Bring up the dev infrastructure

From the repo root, start Postgres, Redis, and LocalStack alongside the Next.js web app. The MCP server itself runs as a separate process — we'll start it in step 3.

bashrepo root
pnpm install
pnpm dev

pnpm dev launches Docker Compose services and the web app on http://localhost:3001.

2. Seed a bearer token in LocalStack Secrets Manager

The MCP server expects a JSON secret at the ARN in CONFIG_SECRET_ARN with a mcpBearerToken field. For local development against LocalStack:

bashseed the secret
export AWS_REGION=us-east-1
export LOCALSTACK_ENDPOINT=http://localhost:4566

aws --endpoint-url=$LOCALSTACK_ENDPOINT secretsmanager create-secret \
  --name cortex/dev/config \
  --secret-string '{"mcpBearerToken":"dev-mcp-token-CHANGE-ME"}'

# Save the returned ARN for the next step
export CONFIG_SECRET_ARN="arn:aws:secretsmanager:us-east-1:000000000000:secret:cortex/dev/config"

3. Build & run the MCP server

bashrepo root
pnpm --filter @cortex/mcp-server build
node packages/mcp-server/dist/index.js

The server listens on http://localhost:3002/mcp. A health probe is at /health (unauthenticated). Logs are JSON; requestId ties together every message in a session.

4. Connect a client

Claude Desktop

Open the config file at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\\Claude\\claude_desktop_config.json (Windows) and add a server entry:

jsonclaude_desktop_config.json
{
  "mcpServers": {
    "cortex": {
      "url": "http://localhost:3002/mcp",
      "headers": {
        "Authorization": "Bearer dev-mcp-token-CHANGE-ME"
      }
    }
  }
}

Restart Claude Desktop. The Cortex tools should appear in the slash-command picker after the next message turn.

Cursor

In Cursor settings → MCP, click Add new MCP server and paste:

jsonCursor MCP settings
{
  "cortex": {
    "url": "http://localhost:3002/mcp",
    "headers": {
      "Authorization": "Bearer dev-mcp-token-CHANGE-ME"
    }
  }
}

MCP Inspector (recommended for debugging)

The official inspector ships as an npx command. Point it at your local server with the bearer token in headers:

bash
npx @modelcontextprotocol/inspector \
  --transport streamable-http \
  --server-url http://localhost:3002/mcp \
  --header "Authorization: Bearer dev-mcp-token-CHANGE-ME"

The inspector renders every tool with its schema, lets you call them one at a time, and shows the raw JSON-RPC payloads on both sides. Use this whenever a client misbehaves — it isolates server bugs from client bugs.

TypeScript — @modelcontextprotocol/sdk

typescriptconnect.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(
  new URL('http://localhost:3002/mcp'),
  {
    requestInit: {
      headers: { Authorization: 'Bearer dev-mcp-token-CHANGE-ME' },
    },
  },
);

const client = new Client(
  { name: 'my-agent', version: '0.1.0' },
  { capabilities: {} },
);

await client.connect(transport);

const result = await client.callTool({
  name: 'memory.recall',
  arguments: { query: "what is sarah's role?", topK: 3 },
});

console.log(result);

Python — mcp SDK

pythonconnect.py
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

URL = "http://localhost:3002/mcp"
HEADERS = {"Authorization": "Bearer dev-mcp-token-CHANGE-ME"}

async def main() -> None:
    async with streamablehttp_client(URL, headers=HEADERS) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool(
                "memory.recall",
                {"query": "what is sarah's role?", "topK": 3},
            )
            print(result)

asyncio.run(main())

5. Verify with a real tool call

From any client above, call memory.recall with a query string. You should see ranked results from the seeded sandbox fixtures. If the call returns HTTP 401, double-check the bearer token matches the seed in step 2. If it returns HTTP 429, see Rate limits — the in-memory store doesn't reset on server restart in the current build.