ClaudeLoops
/
Internal search MCP for your tools
All loops
ProductivityHard 22 min· claude-sonnet-4-5

Internal search MCP for your tools

One MCP server that searches Notion + Linear + Slack from inside Claude Desktop.

NOT DEPLOYEDNOT DEPLOYED
0172ms
Trigger
cron(0 7 * * *) fired · every day · 07:00
02832ms
Agent
claude-sonnet-4-5 · in 932 tok · out 234 tok
03222ms
Tools
http/notion:databases.query → 200 OK · 252ms
0462ms
Verify
schema check · zod v3 passed
0592ms
Output
inbox triage · 6 emails auto-sorted
0652ms
Notify
audit log written · runbook link attached
SUCCESS
0%
0 runs
P50
0ms
median
P95
0ms
tail
AVG COST
per run
LAST OK
never
LAST FAIL
never
none
Latency · last 30 runs0 samples
no runs yet
Latest output · what your users see
Standup · Platform team
  • @norashipped locker health-check endpoint
  • @arunreviewing PR #184 (retry jitter)
  • @linblocked on Stripe webhook signature rotation
  • @devpicking up SEC-914 (S3 bucket audit)
// press Test to run once · Watch live to keep streaming · Deploy to make it real
The problem

Useful context is split across three apps. Switching tabs breaks every train of thought.

The outcome

Inside Claude Desktop, ask 'what did we decide about pricing last week?' — answer pulls from all three, cited.

Ingredients & skills

Secrets
  • NOTION_TOKEN
  • LINEAR_API_KEY
  • SLACK_BOT_TOKEN
Providers
  • Notion
  • Linear
  • Slack
MCP servers
  • (none)
#mcp#search#productivity

How it works

Implements an MCP server with three tools: `search_notion`, `search_linear`, `search_slack`. Bind once in Claude Desktop, search everything from chat.

Step 1

1 — MCP server scaffold

Use `@modelcontextprotocol/sdk`. One process exposes all three tools.

server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server({ name: "internal-search", version: "1.0.0" }, { capabilities: { tools: {} } });
server.tool("search_notion", { query: z.string() }, async ({ query }) => ({ content: [{ type: "text", text: await notionSearch(query) }] }));
server.tool("search_linear", { query: z.string() }, async ({ query }) => ({ content: [{ type: "text", text: await linearSearch(query) }] }));
server.tool("search_slack", { query: z.string() }, async ({ query }) => ({ content: [{ type: "text", text: await slackSearch(query) }] }));
await server.connect(new StdioServerTransport());
Step 2

2 — Bind in Claude Desktop

Secrets resolve from the locker — never paste tokens into the config file.

claude_desktop_config.json
{
  "mcpServers": {
    "internal-search": {
      "command": "node",
      "args": ["./dist/server.js"],
      "env": { "NOTION_TOKEN": "${NOTION_TOKEN}", "LINEAR_API_KEY": "${LINEAR_API_KEY}", "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}" }
    }
  }
}
One-line deploy

The button above runs the same command with your saved config. This is the raw CLI form.

bash
locker deploy internal-search --as mcp-server

Related loops