Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "websets-mcp-codemode",
"owner": {
"name": "Kastalien Research"
},
"plugins": [
{
"name": "websets",
"source": "./",
"description": "Exa Websets for Claude Code — full MCP server (search/execute/status, 110 ops, 12 workflows, SQLite shadow store, webhook receiver) plus a realtime channel that pushes Webset events into your session.",
"version": "1.0.0"
}
]
}
66 changes: 59 additions & 7 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,69 @@
{
"name": "websets",
"description": "Websets webhook channel for Claude Code — receive Exa Webset events (new items, enrichments, idle notifications) and react with full codebase context.",
"version": "1.0.0",
"keywords": ["websets", "exa", "channel", "webhook", "mcp"],
"description": "Exa Websets for Claude Code — full MCP server (search/execute/status, 110 ops, 12 workflows, SQLite shadow store, webhook receiver) plus a realtime channel that pushes Webset events into your session.",
"keywords": ["websets", "exa", "channel", "webhook", "mcp", "search", "enrichment"],
"author": {
"name": "Kastalien Research"
},
"license": "MIT",
"homepage": "https://github.com/Kastalien-Research/websets-mcp-codemode",
"repository": "https://github.com/Kastalien-Research/websets-mcp-codemode",
"mcpServers": {
"websets": {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep bundled skills aligned with renamed MCP server IDs

This manifest now exposes only websets/websets-channel, but the shipped skills still whitelist old tool names like mcp__schwartz13-local__execute and mcp__schwartz13-local__search (see skills/deep-research-item/SKILL.md:6 and skills/verify-item/SKILL.md:6). In plugin mode those tools are unavailable, so invoking /websets:verify-item or /websets:deep-research-item cannot call the Websets execute/search tools and the skill flow breaks.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in efcb1f5.

allowed-tools in the three bundled skills updated to mcp__websets__{execute,search} to match the renamed MCP server. Also dropped the unavailable mcp__airtable__execute from deep-research-item (the Airtable MCP was removed with the rest of servers/), and replaced the hardcoded /workspaces/schwartz13/data/workflow-configs.json path in workflow-config with ${CLAUDE_PLUGIN_DATA}/workflow-configs.json so the skill aligns with where the channel now expects the config.

Drive-by: also fixed two remaining schwartz13-mcp User-Agent strings in src/handlers/github.ts and src/workflows/verifyEnrichments.ts, and rewrote the EXAMPLES.md header for the new identity.


Generated by Claude Code

"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/stdio.js"],
"env": {
"EXA_API_KEY": "${user_config.exa_api_key}",
"EXA_WEBHOOK_SECRET": "${user_config.exa_webhook_secret}",
"WEBSETS_HTTP_PORT": "${user_config.websets_http_port}",
"WEBSETS_DB_PATH": "${user_config.websets_db_path}",
"MANAGE_WEBSETS_DEFAULT_COMPAT_MODE": "${user_config.compat_mode}"
}
},
"websets-channel": {
"command": "npm",
"args": ["--prefix", "${CLAUDE_PLUGIN_ROOT}", "run", "channel"]
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/dist/channel.js"],
"env": {
"WEBSETS_SERVER_URL": "http://127.0.0.1:${user_config.websets_http_port}"
}
}
},
"channels": {
"websets-channel": {
"server": "websets-channel"
"channels": [
{ "server": "websets-channel" }
],
"userConfig": {
"exa_api_key": {
"type": "string",
"title": "Exa API key",
"description": "Required. Get one at https://dashboard.exa.ai. Stored in your system keychain.",
"required": true,
"sensitive": true
},
"exa_webhook_secret": {
"type": "string",
"title": "Exa webhook secret",
"description": "Optional HMAC secret for verifying inbound Exa webhook signatures (Exa-Signature header). Leave blank to skip signature verification.",
"required": false,
"sensitive": true
},
"websets_http_port": {
"type": "number",
"title": "Webhook listener port",
"description": "Local TCP port the websets process binds for /webhooks/exa intake and /webhooks/events SSE. The channel reads this same port. Change if 7860 is taken.",
"default": 7860
},
"websets_db_path": {
"type": "string",
"title": "SQLite database path",
"description": "Path to the local Websets shadow store (items, annotations, events, companies). Defaults to the plugin's persistent data directory.",
"default": "${CLAUDE_PLUGIN_DATA}/websets.db"
},
"compat_mode": {
"type": "string",
"title": "Operation compat mode",
"description": "How strictly callOperation() coerces arguments. 'safe' is forgiving; 'strict' rejects ambiguous inputs.",
"default": "safe"
}
}
}
57 changes: 1 addition & 56 deletions .mcp.json.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"mcpServers": {
"schwartz13-local": {
"websets-local": {
"type": "http",
"url": "http://localhost:7860/mcp"
},
Expand All @@ -10,61 +10,6 @@
"env": {
"WEBSETS_SERVER_URL": "http://localhost:7860"
}
},
"google-workspace": {
"type": "http",
"url": "http://localhost:3000/mcp"
},
"google-workspace-channel": {
"command": "node",
"args": ["servers/google-workspace-mcp/build/index.js", "--channel"],
"env": {
"GOOGLE_CLIENT_ID": "<YOUR_GOOGLE_CLIENT_ID>",
"GOOGLE_CLIENT_SECRET": "<YOUR_GOOGLE_CLIENT_SECRET>",
"GOOGLE_REFRESH_TOKEN": "<YOUR_GOOGLE_REFRESH_TOKEN>",
"GOOGLE_PUBSUB_TOPIC": "<YOUR_PUBSUB_TOPIC>",
"CHANNEL_HOST": "0.0.0.0"
}
},
"thoughtbox": {
"type": "http",
"url": "https://mcp.kastalienresearch.ai/mcp?key=<YOUR_THOUGHTBOX_API_KEY>"
},
"thoughtbox-gateway": {
"type": "http",
"url": "http://localhost:8000/mcp"
},
"airtable": {
"type": "http",
"url": "http://localhost:3500/mcp"
},
"github-channel": {
"command": "node",
"args": ["servers/github-channel/build/channel.js"],
"env": {
"CHANNEL_HOST": "0.0.0.0",
"CHANNEL_PORT": "3003"
}
},
"linear-channel": {
"command": "node",
"args": ["servers/linear-channel/build/channel.js"],
"env": {
"LINEAR_API_KEY": "<YOUR_LINEAR_API_KEY>",
"CHANNEL_HOST": "0.0.0.0",
"CHANNEL_PORT": "3004"
}
},
"airtable-channel": {
"command": "node",
"args": ["servers/effect-airtable-mcp/build/channel.js"],
"env": {
"AIRTABLE_API_KEY": "<YOUR_AIRTABLE_API_KEY>",
"AIRTABLE_WATCH_BASES": "<YOUR_AIRTABLE_BASE_IDS>",
"AIRTABLE_WEBHOOK_URL": "<YOUR_CODESPACE_WEBHOOK_URL>",
"CHANNEL_HOST": "0.0.0.0",
"CHANNEL_PORT": "3002"
}
}
}
}
35 changes: 22 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
# CLAUDE.md

This repository is currently a Docker-first MCP server for Exa Websets.
This repository is the **Websets Claude Code plugin**: a stdio MCP server + a realtime channel, packaged via `.claude-plugin/plugin.json`. It also ships an HTTP MCP entrypoint (`dist/index.js`) for Docker/hosted use, but the plugin path (`dist/stdio.js`) is now primary.

## Working Assumptions

- Treat Docker and HTTP transport as the primary runtime.
- Treat the plugin install path (`dist/stdio.js` + `dist/channel.js` spawned as stdio subprocesses) as the primary runtime.
- HTTP/Docker is a secondary path for hosted deployments.
- Do not treat this as a published npm package.
- If we add a non-Docker path later, that should be designed deliberately rather than inferred
from stale docs.
- The `servers/` siblings (github-channel, linear-channel, agentmail, google-workspace, effect-airtable) have been removed from this repo.

## Useful Commands

```bash
docker compose up --build
npm install
npm run build
npm run stdio # run the plugin entrypoint locally
npm run channel # run the channel locally
npm start # run the HTTP entrypoint
docker compose up --build
npm test
npm run test:e2e
```

## Architecture Snapshot

- `src/index.ts` boots the Express app and listens on port `7860`.
- `src/server.ts` exposes MCP over `StreamableHTTPServerTransport` at `/mcp`.
- `.claude-plugin/plugin.json` — plugin manifest (mcpServers, channels, userConfig).
- `.claude-plugin/marketplace.json` — marketplace entry for this plugin.
- `src/stdio.ts` boots the **plugin entrypoint**: stdio MCP transport + in-process Express webhook listener on `WEBSETS_HTTP_PORT` (default `7860`).
- `src/index.ts` boots the HTTP entrypoint (Docker/hosted) on the same port.
- `src/server.ts` exports `createMcpServer()`, `startWebhookListener()`, and the HTTP `createServer()` factory. MCP over `StreamableHTTPServerTransport` at `/mcp` is HTTP-only.
- `src/tools/operations.ts` exports the `OPERATIONS` registry, `dispatchOperation()`, and supporting utilities.
- `src/tools/catalog.ts` builds a searchable index of all 60 operations + workflows.
- `src/tools/catalog.ts` builds a searchable index of all 110 operations + workflows.
- `src/tools/searchTool.ts` registers the Code Mode `search` tool (operation discovery).
- `src/tools/sandbox.ts` executes LLM-generated JS in a `vm` sandbox with `callOperation` injected.
- `src/tools/executeTool.ts` registers the Code Mode `execute` tool (code execution).
Expand All @@ -34,22 +41,24 @@ npm run test:e2e
- `src/webhooks/eventBus.ts` decouples webhook ingestion from SSE delivery with SQLite persistence.
- `src/store/db.ts` SQLite shadow store for Webset items + local annotations layer.
- `src/store/operations.ts` exposes `store.annotate`, `store.getItem`, `store.listUninvestigated`, `store.query`.
- `src/channel.ts` standalone stdio MCP channel bridge for Claude Code webhook notifications.
- `src/channel.ts` stdio channel server. Declares `claude/channel`, subscribes to the local SSE stream, emits `notifications/claude/channel`.
- `skills/{deep-research-item,verify-item,workflow-config}/` plugin-bundled skills, namespaced as `/websets:<name>`.

### MCP Tools

The server exposes three tools:
The `websets` MCP server exposes three tools:
1. **`search`** — Code Mode discovery: find operations by keyword/domain with brief/detailed/full schemas
2. **`execute`** — Code Mode execution: run JS code with `callOperation()` in a sandboxed `vm`
3. **`status`** — Account overview: webset counts, running tasks, active monitors, server capabilities

The `websets-channel` MCP server exposes no tools — it only pushes events via `notifications/claude/channel`.

## Verification

Do not use curl to verify behavior. Use the MCP `execute` tool with `callOperation()` — that's the Code Mode pattern this server is built on.

## Agent Guidance

- Keep docs aligned with Docker-first operation.
- Keep docs aligned with the plugin-first install path. HTTP/Docker stays documented as a secondary option.
- Prefer removing stale local assistant scaffolding over preserving broken historical flows.
- For the load-bearing skill during this refactor, use
`/workspaces/openchatwidget/.agents/skills/code-mode-servers/SKILL.md`.
- For the load-bearing skill during plugin/channel work, use the in-repo `channels-mcp`, `channel-bridge`, and `code-mode-servers` skills under `.claude/skills/`.
15 changes: 8 additions & 7 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# schwartz13 Examples
# Websets Examples

This server is Docker-first and exposes a single HTTP MCP endpoint at `/mcp`.
All examples below assume the server is already running.
The primary install path is the Claude Code plugin (`/plugin install websets@websets-mcp-codemode`); the HTTP/Docker path below is for hosted deployments.

## Start the Server
## Plugin install

See [README.md](./README.md) for the plugin install flow. Once installed, every `execute` example below works as-is — Claude calls the bundled `mcp__websets__execute` tool with `callOperation(...)`.

## Docker / HTTP path

```bash
EXA_API_KEY=your-key docker compose up --build
```

## Client Configuration

```json
{
"mcpServers": {
"schwartz13": {
"websets": {
"type": "http",
"url": "http://localhost:7860/mcp"
}
Expand Down
Loading
Loading