diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 00000000..288d7d13 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,39 @@ +{ + "name": "cursor-plugins", + "interface": { "displayName": "Cursor plugins" }, + "plugins": [ + {"name": "teaching", "source": "./teaching", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "continual-learning", "source": "./continual-learning", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "cursor-team-kit", "source": "./cursor-team-kit", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "thermos", "source": "./thermos", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "create-plugin", "source": "./create-plugin", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "ralph-loop", "source": "./ralph-loop", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "agent-compatibility", "source": "./agent-compatibility", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "cli-for-agent", "source": "./cli-for-agent", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "pr-review-canvas", "source": "./pr-review-canvas", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "docs-canvas", "source": "./docs-canvas", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "cursor-sdk", "source": "./cursor-sdk", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "orchestrate", "source": "./orchestrate", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "pstack", "source": "./pstack", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "gmail", "source": "./third_party/gmail", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "google-drive", "source": "./third_party/google-drive", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "google-calendar", "source": "./third_party/google-calendar", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "gong", "source": "./third_party/gong", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "salesforce", "source": "./third_party/salesforce", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "playwright", "source": "./third_party/playwright", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "github", "source": "./third_party/github", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "ashby", "source": "./third_party/ashby", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "hubspot", "source": "./third_party/hubspot", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "intercom", "source": "./third_party/intercom", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "zoom", "source": "./third_party/zoom", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "x", "source": "./third_party/x", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "clay", "source": "./third_party/clay", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "circleback", "source": "./third_party/circleback", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "docusign", "source": "./third_party/docusign", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "navan", "source": "./third_party/navan", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "profound", "source": "./third_party/profound", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "juicebox", "source": "./third_party/juicebox", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "outreach", "source": "./third_party/outreach", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"}, + {"name": "amplemarket", "source": "./third_party/amplemarket", "policy": {"installation": "AVAILABLE", "authentication": "ON_INSTALL"}, "category": "Productivity"} + ] +} diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..63b988d8 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,40 @@ +{ + "name": "cursor-plugins", + "description": "Cursor plugins.", + "owner": { "name": "Rudimar Ronsoni" }, + "plugins": [ + {"name": "teaching", "source": "./teaching", "strict": false}, + {"name": "continual-learning", "source": "./continual-learning", "strict": false}, + {"name": "cursor-team-kit", "source": "./cursor-team-kit", "strict": false}, + {"name": "thermos", "source": "./thermos", "strict": false}, + {"name": "create-plugin", "source": "./create-plugin", "strict": false}, + {"name": "ralph-loop", "source": "./ralph-loop", "strict": false}, + {"name": "agent-compatibility", "source": "./agent-compatibility", "strict": false}, + {"name": "cli-for-agent", "source": "./cli-for-agent", "strict": false}, + {"name": "pr-review-canvas", "source": "./pr-review-canvas", "strict": false}, + {"name": "docs-canvas", "source": "./docs-canvas", "strict": false}, + {"name": "cursor-sdk", "source": "./cursor-sdk", "strict": false}, + {"name": "orchestrate", "source": "./orchestrate", "strict": false}, + {"name": "pstack", "source": "./pstack", "strict": false}, + {"name": "gmail", "source": "./third_party/gmail", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "google-drive", "source": "./third_party/google-drive", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "google-calendar", "source": "./third_party/google-calendar", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "gong", "source": "./third_party/gong", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "salesforce", "source": "./third_party/salesforce", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "playwright", "source": "./third_party/playwright", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "github", "source": "./third_party/github", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "ashby", "source": "./third_party/ashby", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "hubspot", "source": "./third_party/hubspot", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "intercom", "source": "./third_party/intercom", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "zoom", "source": "./third_party/zoom", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "x", "source": "./third_party/x", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "clay", "source": "./third_party/clay", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "circleback", "source": "./third_party/circleback", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "docusign", "source": "./third_party/docusign", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "navan", "source": "./third_party/navan", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "profound", "source": "./third_party/profound", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "juicebox", "source": "./third_party/juicebox", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "outreach", "source": "./third_party/outreach", "strict": false, "mcpServers": "./mcp.json"}, + {"name": "amplemarket", "source": "./third_party/amplemarket", "strict": false, "mcpServers": "./mcp.json"} + ] +} diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json new file mode 100644 index 00000000..d7c7f7ec --- /dev/null +++ b/.github/plugin/marketplace.json @@ -0,0 +1,39 @@ +{ + "name": "cursor-plugins", + "owner": { "name": "Rudimar Ronsoni" }, + "plugins": [ + {"name": "teaching", "source": "./teaching"}, + {"name": "continual-learning", "source": "./continual-learning"}, + {"name": "cursor-team-kit", "source": "./cursor-team-kit"}, + {"name": "thermos", "source": "./thermos"}, + {"name": "create-plugin", "source": "./create-plugin"}, + {"name": "ralph-loop", "source": "./ralph-loop"}, + {"name": "agent-compatibility", "source": "./agent-compatibility"}, + {"name": "cli-for-agent", "source": "./cli-for-agent"}, + {"name": "pr-review-canvas", "source": "./pr-review-canvas"}, + {"name": "docs-canvas", "source": "./docs-canvas"}, + {"name": "cursor-sdk", "source": "./cursor-sdk"}, + {"name": "orchestrate", "source": "./orchestrate"}, + {"name": "pstack", "source": "./pstack"}, + {"name": "gmail", "source": "./third_party/gmail"}, + {"name": "google-drive", "source": "./third_party/google-drive"}, + {"name": "google-calendar", "source": "./third_party/google-calendar"}, + {"name": "gong", "source": "./third_party/gong"}, + {"name": "salesforce", "source": "./third_party/salesforce"}, + {"name": "playwright", "source": "./third_party/playwright"}, + {"name": "github", "source": "./third_party/github"}, + {"name": "ashby", "source": "./third_party/ashby"}, + {"name": "hubspot", "source": "./third_party/hubspot"}, + {"name": "intercom", "source": "./third_party/intercom"}, + {"name": "zoom", "source": "./third_party/zoom"}, + {"name": "x", "source": "./third_party/x"}, + {"name": "clay", "source": "./third_party/clay"}, + {"name": "circleback", "source": "./third_party/circleback"}, + {"name": "docusign", "source": "./third_party/docusign"}, + {"name": "navan", "source": "./third_party/navan"}, + {"name": "profound", "source": "./third_party/profound"}, + {"name": "juicebox", "source": "./third_party/juicebox"}, + {"name": "outreach", "source": "./third_party/outreach"}, + {"name": "amplemarket", "source": "./third_party/amplemarket"} + ] +} diff --git a/.grok-plugin/marketplace.json b/.grok-plugin/marketplace.json new file mode 100644 index 00000000..df105d91 --- /dev/null +++ b/.grok-plugin/marketplace.json @@ -0,0 +1,40 @@ +{ + "name": "cursor-plugins", + "description": "Cursor plugins.", + "owner": { "name": "Rudimar Ronsoni" }, + "plugins": [ + {"name": "teaching", "source": "./teaching"}, + {"name": "continual-learning", "source": "./continual-learning"}, + {"name": "cursor-team-kit", "source": "./cursor-team-kit"}, + {"name": "thermos", "source": "./thermos"}, + {"name": "create-plugin", "source": "./create-plugin"}, + {"name": "ralph-loop", "source": "./ralph-loop"}, + {"name": "agent-compatibility", "source": "./agent-compatibility"}, + {"name": "cli-for-agent", "source": "./cli-for-agent"}, + {"name": "pr-review-canvas", "source": "./pr-review-canvas"}, + {"name": "docs-canvas", "source": "./docs-canvas"}, + {"name": "cursor-sdk", "source": "./cursor-sdk"}, + {"name": "orchestrate", "source": "./orchestrate"}, + {"name": "pstack", "source": "./pstack"}, + {"name": "gmail", "source": "./third_party/gmail"}, + {"name": "google-drive", "source": "./third_party/google-drive"}, + {"name": "google-calendar", "source": "./third_party/google-calendar"}, + {"name": "gong", "source": "./third_party/gong"}, + {"name": "salesforce", "source": "./third_party/salesforce"}, + {"name": "playwright", "source": "./third_party/playwright"}, + {"name": "github", "source": "./third_party/github"}, + {"name": "ashby", "source": "./third_party/ashby"}, + {"name": "hubspot", "source": "./third_party/hubspot"}, + {"name": "intercom", "source": "./third_party/intercom"}, + {"name": "zoom", "source": "./third_party/zoom"}, + {"name": "x", "source": "./third_party/x"}, + {"name": "clay", "source": "./third_party/clay"}, + {"name": "circleback", "source": "./third_party/circleback"}, + {"name": "docusign", "source": "./third_party/docusign"}, + {"name": "navan", "source": "./third_party/navan"}, + {"name": "profound", "source": "./third_party/profound"}, + {"name": "juicebox", "source": "./third_party/juicebox"}, + {"name": "outreach", "source": "./third_party/outreach"}, + {"name": "amplemarket", "source": "./third_party/amplemarket"} + ] +} diff --git a/README.md b/README.md index 5408d433..3051b520 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,36 @@ # Cursor plugins +Fork of [cursor/plugins](https://github.com/cursor/plugins). Each plugin folder is installable on Claude Code, Grok, Codex, and OpenCode. Skill and MCP files stay where they are. + +```sh +claude plugin marketplace add rudironsoni/cursor-plugins-for-all-harnesses +claude plugin install teaching@cursor-plugins + +grok plugin marketplace add rudironsoni/cursor-plugins-for-all-harnesses +grok plugin install teaching --trust + +codex plugin marketplace add rudironsoni/cursor-plugins-for-all-harnesses +codex plugin install teaching +``` + +OpenCode has no marketplace. Every plugin ships an `opencode.js`. After you have the plugin folder, add it in `opencode.json`: + +```json +{ "plugin": ["./teaching/opencode.js"] } +``` + +MCP plugins (everything under `third_party/`) register their server via the `config` hook, so there is nothing else to configure. Restart OpenCode after enabling, because MCP registration happens at startup. Credentials use `{env:VAR}` interpolation, for example `GITHUB_PERSONAL_ACCESS_TOKEN` for `third_party/github`. Services that use OAuth client credentials (`docusign`, `gong`, `hubspot`, `salesforce`, `x`, `x-ads`, `zoom`) register an `oauth` block; run `opencode mcp auth ` to complete the flow. + +Skill plugins (the first-party folders such as `teaching/`, plus `advisor`) keep their skills in `./skills/`. Copy or symlink them into `.opencode/skills`, `.claude/skills`, or `.agents/skills` for discovery. + +Or use the community installer, which reads this repo's Claude catalog: + +```sh +npx opencode-market add rudironsoni/cursor-plugins-for-all-harnesses +npx opencode-market install teaching@cursor-plugins --opencode +``` + + Official Cursor plugins for popular developer tools, frameworks, and SaaS products. Each plugin is a standalone directory at the repository root with its own `.cursor-plugin/plugin.json` manifest. ## Plugins diff --git a/advisor/opencode.js b/advisor/opencode.js new file mode 100644 index 00000000..cee59904 --- /dev/null +++ b/advisor/opencode.js @@ -0,0 +1,50 @@ +// advisor - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const AdvisorPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["advisor"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["advisor-subagent.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + + }, + + // afterFileEdit -> tool.execute.after (filter edit tools) + "tool.execute.after": async (input, output) => { + const editTools = ["edit", "write", "patch", "replace"]; + if (editTools.includes(input.tool)) { + // // Would run: mark-pending.sh + } + }, + // afterAgentResponse -> chat.message + "chat.message": async (input, output) => { + // // Would run: capture-response.sh + }, + // subagentStop -> event listener for subagent + event: async ({ event }) => { + if (event.type === "session.agent.stop" && event.agent === "advisor-subagent") { + // // Would run: record-consult.sh + } + }, + // stop -> session.idle + "session.idle": async (input, output) => { + // // Would run: stop-hook.sh + }, + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default AdvisorPlugin; diff --git a/agent-compatibility/.claude-plugin/plugin.json b/agent-compatibility/.claude-plugin/plugin.json new file mode 100644 index 00000000..772cb6fb --- /dev/null +++ b/agent-compatibility/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "agent-compatibility", + "version": "1.0.0", + "description": "CLI-backed repo compatibility scans plus agents that audit startup, validation, and docs against reality.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/agent-compatibility/.codex-plugin/plugin.json b/agent-compatibility/.codex-plugin/plugin.json new file mode 100644 index 00000000..a5e613b7 --- /dev/null +++ b/agent-compatibility/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "agent-compatibility", + "version": "1.0.0", + "description": "CLI-backed repo compatibility scans plus agents that audit startup, validation, and docs against reality.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/agent-compatibility/.grok-plugin/plugin.json b/agent-compatibility/.grok-plugin/plugin.json new file mode 100644 index 00000000..772cb6fb --- /dev/null +++ b/agent-compatibility/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "agent-compatibility", + "version": "1.0.0", + "description": "CLI-backed repo compatibility scans plus agents that audit startup, validation, and docs against reality.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/agent-compatibility/opencode.js b/agent-compatibility/opencode.js new file mode 100644 index 00000000..b7f142e4 --- /dev/null +++ b/agent-compatibility/opencode.js @@ -0,0 +1,30 @@ +// agent-compatibility - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const AgentCompatibilityPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["check-agent-compatibility"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["compatibility-scan-review.md","docs-reliability-review.md","startup-review.md","validation-review.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default AgentCompatibilityPlugin; diff --git a/cli-for-agent/.claude-plugin/plugin.json b/cli-for-agent/.claude-plugin/plugin.json new file mode 100644 index 00000000..86c56856 --- /dev/null +++ b/cli-for-agent/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cli-for-agent", + "version": "1.0.0", + "description": "Patterns for designing CLIs that coding agents can run reliably: non-interactive flags, layered help with examples, pipelines, actionable errors, idempotency, and dry-run.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cli-for-agent/.codex-plugin/plugin.json b/cli-for-agent/.codex-plugin/plugin.json new file mode 100644 index 00000000..9c94635b --- /dev/null +++ b/cli-for-agent/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "cli-for-agent", + "version": "1.0.0", + "description": "Patterns for designing CLIs that coding agents can run reliably: non-interactive flags, layered help with examples, pipelines, actionable errors, idempotency, and dry-run.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/cli-for-agent/.grok-plugin/plugin.json b/cli-for-agent/.grok-plugin/plugin.json new file mode 100644 index 00000000..86c56856 --- /dev/null +++ b/cli-for-agent/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cli-for-agent", + "version": "1.0.0", + "description": "Patterns for designing CLIs that coding agents can run reliably: non-interactive flags, layered help with examples, pipelines, actionable errors, idempotency, and dry-run.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cli-for-agent/opencode.js b/cli-for-agent/opencode.js new file mode 100644 index 00000000..066f2fe2 --- /dev/null +++ b/cli-for-agent/opencode.js @@ -0,0 +1,24 @@ +// cli-for-agent - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const CliForAgentPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["cli-for-agents"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default CliForAgentPlugin; diff --git a/continual-learning/.claude-plugin/plugin.json b/continual-learning/.claude-plugin/plugin.json new file mode 100644 index 00000000..04db9d64 --- /dev/null +++ b/continual-learning/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "continual-learning", + "version": "1.0.0", + "description": "Incrementally learns durable user preferences and workspace facts from transcript changes and keeps AGENTS.md up to date with plain bullet points.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/continual-learning/.codex-plugin/plugin.json b/continual-learning/.codex-plugin/plugin.json new file mode 100644 index 00000000..25f94a84 --- /dev/null +++ b/continual-learning/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "continual-learning", + "version": "1.0.0", + "description": "Incrementally learns durable user preferences and workspace facts from transcript changes and keeps AGENTS.md up to date with plain bullet points.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/continual-learning/.grok-plugin/plugin.json b/continual-learning/.grok-plugin/plugin.json new file mode 100644 index 00000000..04db9d64 --- /dev/null +++ b/continual-learning/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "continual-learning", + "version": "1.0.0", + "description": "Incrementally learns durable user preferences and workspace facts from transcript changes and keeps AGENTS.md up to date with plain bullet points.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/continual-learning/opencode.js b/continual-learning/opencode.js new file mode 100644 index 00000000..77f40fee --- /dev/null +++ b/continual-learning/opencode.js @@ -0,0 +1,33 @@ +// continual-learning - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const ContinualLearningPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["continual-learning"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["agents-memory-updater.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + + }, + + // stop -> session.idle + "session.idle": async (input, output) => { + // // Hook script reference + }, + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default ContinualLearningPlugin; diff --git a/create-plugin/.claude-plugin/plugin.json b/create-plugin/.claude-plugin/plugin.json new file mode 100644 index 00000000..6b281ea9 --- /dev/null +++ b/create-plugin/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "create-plugin", + "version": "1.0.0", + "description": "Scaffold and validate new agent plugins. Handles directory setup, manifest generation, and pre-submission quality checks for the marketplace.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/create-plugin/.codex-plugin/plugin.json b/create-plugin/.codex-plugin/plugin.json new file mode 100644 index 00000000..a9b69831 --- /dev/null +++ b/create-plugin/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "create-plugin", + "version": "1.0.0", + "description": "Scaffold and validate new agent plugins. Handles directory setup, manifest generation, and pre-submission quality checks for the marketplace.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/create-plugin/.grok-plugin/plugin.json b/create-plugin/.grok-plugin/plugin.json new file mode 100644 index 00000000..6b281ea9 --- /dev/null +++ b/create-plugin/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "create-plugin", + "version": "1.0.0", + "description": "Scaffold and validate new agent plugins. Handles directory setup, manifest generation, and pre-submission quality checks for the marketplace.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/create-plugin/opencode.js b/create-plugin/opencode.js new file mode 100644 index 00000000..3fc8740f --- /dev/null +++ b/create-plugin/opencode.js @@ -0,0 +1,34 @@ +// create-plugin - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const CreatePluginPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["create-plugin-scaffold","review-plugin-submission"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["plugin-architect.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + config.instructions ??= []; + const rulesDir = join(PLUGIN_DIR, "rules"); + for (const rule of ["plugin-quality-gates.mdc"]) { + config.instructions.push(join(rulesDir, rule)); + } + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default CreatePluginPlugin; diff --git a/cursor-sdk/.claude-plugin/plugin.json b/cursor-sdk/.claude-plugin/plugin.json new file mode 100644 index 00000000..262eac8b --- /dev/null +++ b/cursor-sdk/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cursor-sdk", + "version": "1.0.0", + "description": "Build apps, scripts, and automations with the TypeScript SDK.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cursor-sdk/.codex-plugin/plugin.json b/cursor-sdk/.codex-plugin/plugin.json new file mode 100644 index 00000000..ca280749 --- /dev/null +++ b/cursor-sdk/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "cursor-sdk", + "version": "1.0.0", + "description": "Build apps, scripts, and automations with the TypeScript SDK.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/cursor-sdk/.grok-plugin/plugin.json b/cursor-sdk/.grok-plugin/plugin.json new file mode 100644 index 00000000..262eac8b --- /dev/null +++ b/cursor-sdk/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cursor-sdk", + "version": "1.0.0", + "description": "Build apps, scripts, and automations with the TypeScript SDK.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cursor-sdk/opencode.js b/cursor-sdk/opencode.js new file mode 100644 index 00000000..22848028 --- /dev/null +++ b/cursor-sdk/opencode.js @@ -0,0 +1,24 @@ +// cursor-sdk - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const CursorSdkPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["cursor-sdk"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default CursorSdkPlugin; diff --git a/cursor-team-kit/.claude-plugin/plugin.json b/cursor-team-kit/.claude-plugin/plugin.json new file mode 100644 index 00000000..7000b006 --- /dev/null +++ b/cursor-team-kit/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cursor-team-kit", + "version": "1.2.0", + "description": "Internal engineering team workflows for CI, code review, shipping, control-cli, control-ui, verify-this, test reliability, code cleanup, and work summaries. Designed to work without requiring third-party service integrations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cursor-team-kit/.codex-plugin/plugin.json b/cursor-team-kit/.codex-plugin/plugin.json new file mode 100644 index 00000000..8a22e2e0 --- /dev/null +++ b/cursor-team-kit/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "cursor-team-kit", + "version": "1.2.0", + "description": "Internal engineering team workflows for CI, code review, shipping, control-cli, control-ui, verify-this, test reliability, code cleanup, and work summaries. Designed to work without requiring third-party service integrations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/cursor-team-kit/.grok-plugin/plugin.json b/cursor-team-kit/.grok-plugin/plugin.json new file mode 100644 index 00000000..7000b006 --- /dev/null +++ b/cursor-team-kit/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "cursor-team-kit", + "version": "1.2.0", + "description": "Internal engineering team workflows for CI, code review, shipping, control-cli, control-ui, verify-this, test reliability, code cleanup, and work summaries. Designed to work without requiring third-party service integrations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/cursor-team-kit/opencode.js b/cursor-team-kit/opencode.js new file mode 100644 index 00000000..40f4fa94 --- /dev/null +++ b/cursor-team-kit/opencode.js @@ -0,0 +1,34 @@ +// cursor-team-kit - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const CursorTeamKitPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["check-compiler-errors","control-cli","control-ui","deslop","fix-ci","fix-merge-conflicts","get-pr-comments","loop-on-ci","make-pr-easy-to-review","new-branch-and-pr","pr-review-canvas","review-and-ship","run-smoke-tests","thermo-nuclear-code-quality-review","verify-this","weekly-review","what-did-i-get-done","workflow-from-chats"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["ci-watcher.md","thermo-nuclear-code-quality-review.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + config.instructions ??= []; + const rulesDir = join(PLUGIN_DIR, "rules"); + for (const rule of ["no-inline-imports.mdc","typescript-exhaustive-switch.mdc"]) { + config.instructions.push(join(rulesDir, rule)); + } + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default CursorTeamKitPlugin; diff --git a/docs-canvas/.claude-plugin/plugin.json b/docs-canvas/.claude-plugin/plugin.json new file mode 100644 index 00000000..04461363 --- /dev/null +++ b/docs-canvas/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "docs-canvas", + "version": "0.1.0", + "description": "Render documentation as a navigable canvas.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/docs-canvas/.codex-plugin/plugin.json b/docs-canvas/.codex-plugin/plugin.json new file mode 100644 index 00000000..86adfce7 --- /dev/null +++ b/docs-canvas/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "docs-canvas", + "version": "0.1.0", + "description": "Render documentation as a navigable canvas.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/docs-canvas/.grok-plugin/plugin.json b/docs-canvas/.grok-plugin/plugin.json new file mode 100644 index 00000000..04461363 --- /dev/null +++ b/docs-canvas/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "docs-canvas", + "version": "0.1.0", + "description": "Render documentation as a navigable canvas.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/docs-canvas/opencode.js b/docs-canvas/opencode.js new file mode 100644 index 00000000..1f5364fc --- /dev/null +++ b/docs-canvas/opencode.js @@ -0,0 +1,24 @@ +// docs-canvas - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const DocsCanvasPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["docs-canvas"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default DocsCanvasPlugin; diff --git a/orchestrate/.claude-plugin/plugin.json b/orchestrate/.claude-plugin/plugin.json new file mode 100644 index 00000000..69195f82 --- /dev/null +++ b/orchestrate/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "orchestrate", + "version": "1.1.0", + "description": "Fan a large task out across parallel cloud agents via the Cursor SDK: planners publish tasks, workers hand off back up, and a script reconciles the tree from disk and git.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/orchestrate/.codex-plugin/plugin.json b/orchestrate/.codex-plugin/plugin.json new file mode 100644 index 00000000..65c643b1 --- /dev/null +++ b/orchestrate/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "orchestrate", + "version": "1.1.0", + "description": "Fan a large task out across parallel cloud agents via the Cursor SDK: planners publish tasks, workers hand off back up, and a script reconciles the tree from disk and git.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/orchestrate/.grok-plugin/plugin.json b/orchestrate/.grok-plugin/plugin.json new file mode 100644 index 00000000..69195f82 --- /dev/null +++ b/orchestrate/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "orchestrate", + "version": "1.1.0", + "description": "Fan a large task out across parallel cloud agents via the Cursor SDK: planners publish tasks, workers hand off back up, and a script reconciles the tree from disk and git.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/orchestrate/opencode.js b/orchestrate/opencode.js new file mode 100644 index 00000000..a80e36d4 --- /dev/null +++ b/orchestrate/opencode.js @@ -0,0 +1,24 @@ +// orchestrate - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const OrchestratePlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["orchestrate"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default OrchestratePlugin; diff --git a/pr-review-canvas/.claude-plugin/plugin.json b/pr-review-canvas/.claude-plugin/plugin.json new file mode 100644 index 00000000..914c7919 --- /dev/null +++ b/pr-review-canvas/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "pr-review-canvas", + "version": "0.1.0", + "description": "Render PR diffs as review canvases grouped by importance.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/pr-review-canvas/.codex-plugin/plugin.json b/pr-review-canvas/.codex-plugin/plugin.json new file mode 100644 index 00000000..658f18bd --- /dev/null +++ b/pr-review-canvas/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "pr-review-canvas", + "version": "0.1.0", + "description": "Render PR diffs as review canvases grouped by importance.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/pr-review-canvas/.grok-plugin/plugin.json b/pr-review-canvas/.grok-plugin/plugin.json new file mode 100644 index 00000000..914c7919 --- /dev/null +++ b/pr-review-canvas/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "pr-review-canvas", + "version": "0.1.0", + "description": "Render PR diffs as review canvases grouped by importance.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/pr-review-canvas/opencode.js b/pr-review-canvas/opencode.js new file mode 100644 index 00000000..dacefb45 --- /dev/null +++ b/pr-review-canvas/opencode.js @@ -0,0 +1,24 @@ +// pr-review-canvas - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const PrReviewCanvasPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["pr-review-canvas"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default PrReviewCanvasPlugin; diff --git a/pstack/.claude-plugin/plugin.json b/pstack/.claude-plugin/plugin.json new file mode 100644 index 00000000..3519dee5 --- /dev/null +++ b/pstack/.claude-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "pstack", + "version": "0.14.1", + "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence.", + "author": { + "name": "Lauren Tan" + } +} diff --git a/pstack/.codex-plugin/plugin.json b/pstack/.codex-plugin/plugin.json new file mode 100644 index 00000000..fa35cc29 --- /dev/null +++ b/pstack/.codex-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "pstack", + "version": "0.14.1", + "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence.", + "author": { + "name": "Lauren Tan" + }, + "skills": "./skills/" +} diff --git a/pstack/.grok-plugin/plugin.json b/pstack/.grok-plugin/plugin.json new file mode 100644 index 00000000..3519dee5 --- /dev/null +++ b/pstack/.grok-plugin/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "pstack", + "version": "0.14.1", + "description": "if you want to go fast, go deep first. pstack helps you write less, but higher quality code. rigorous agent workflows you can parallelize with confidence.", + "author": { + "name": "Lauren Tan" + } +} diff --git a/pstack/opencode.js b/pstack/opencode.js new file mode 100644 index 00000000..dd5b6887 --- /dev/null +++ b/pstack/opencode.js @@ -0,0 +1,30 @@ +// pstack - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const PstackPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["architect","arena","automate-me","blast-radius","bro","create-verification-skill","figure-it-out","how","interrogate","maintain-verification-skill","make-bot-ui","no-comments","poteto-mode","principle-boundary-discipline","principle-build-the-lever","principle-encode-lessons-in-structure","principle-exhaust-the-design-space","principle-experience-first","principle-fix-root-causes","principle-foundational-thinking","principle-guard-the-context-window","principle-laziness-protocol","principle-make-operations-idempotent","principle-migrate-callers-then-delete-legacy-apis","principle-minimize-reader-load","principle-model-the-domain","principle-never-block-on-the-human","principle-outcome-oriented-execution","principle-prove-it-works","principle-redesign-from-first-principles","principle-separate-before-serializing-shared-state","principle-sequence-verifiable-units","principle-subtract-before-you-add","principle-type-system-discipline","recall","reflect","setup-pstack","show-me-your-work","swarm","tdd","teach","technical-writing","typescript-best-practices","unslop","why"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["comment-sicko.md","poteto-agent.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default PstackPlugin; diff --git a/ralph-loop/.claude-plugin/plugin.json b/ralph-loop/.claude-plugin/plugin.json new file mode 100644 index 00000000..e75404bd --- /dev/null +++ b/ralph-loop/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "ralph-loop", + "version": "1.0.0", + "description": "Continuous self-referential AI loops for iterative development, implementing the Ralph Wiggum technique. Run the agent in a while-true loop with the same prompt until task completion.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/ralph-loop/.codex-plugin/plugin.json b/ralph-loop/.codex-plugin/plugin.json new file mode 100644 index 00000000..bf8d0fab --- /dev/null +++ b/ralph-loop/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "ralph-loop", + "version": "1.0.0", + "description": "Continuous self-referential AI loops for iterative development, implementing the Ralph Wiggum technique. Run the agent in a while-true loop with the same prompt until task completion.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/ralph-loop/.grok-plugin/plugin.json b/ralph-loop/.grok-plugin/plugin.json new file mode 100644 index 00000000..e75404bd --- /dev/null +++ b/ralph-loop/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "ralph-loop", + "version": "1.0.0", + "description": "Continuous self-referential AI loops for iterative development, implementing the Ralph Wiggum technique. Run the agent in a while-true loop with the same prompt until task completion.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/ralph-loop/opencode.js b/ralph-loop/opencode.js new file mode 100644 index 00000000..fc11d104 --- /dev/null +++ b/ralph-loop/opencode.js @@ -0,0 +1,31 @@ +// ralph-loop - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const RalphLoopPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["cancel-ralph","ralph-loop","ralph-loop-help"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + // afterAgentResponse -> chat.message + "chat.message": async (input, output) => { + // // Hook script reference + }, + // stop -> session.idle + "session.idle": async (input, output) => { + // // Hook script reference + }, + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default RalphLoopPlugin; diff --git a/teaching/.claude-plugin/plugin.json b/teaching/.claude-plugin/plugin.json new file mode 100644 index 00000000..6ef980ef --- /dev/null +++ b/teaching/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "teaching", + "version": "1.0.0", + "description": "Skill mapping, practice plans, and learning retrospectives. Builds personalized roadmaps with milestones and practice checkpoints, and runs periodic reviews to adjust based on progress.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/teaching/.codex-plugin/plugin.json b/teaching/.codex-plugin/plugin.json new file mode 100644 index 00000000..1deaff81 --- /dev/null +++ b/teaching/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "teaching", + "version": "1.0.0", + "description": "Skill mapping, practice plans, and learning retrospectives. Builds personalized roadmaps with milestones and practice checkpoints, and runs periodic reviews to adjust based on progress.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/teaching/.grok-plugin/plugin.json b/teaching/.grok-plugin/plugin.json new file mode 100644 index 00000000..6ef980ef --- /dev/null +++ b/teaching/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "teaching", + "version": "1.0.0", + "description": "Skill mapping, practice plans, and learning retrospectives. Builds personalized roadmaps with milestones and practice checkpoints, and runs periodic reviews to adjust based on progress.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/teaching/opencode.js b/teaching/opencode.js new file mode 100644 index 00000000..0db6a826 --- /dev/null +++ b/teaching/opencode.js @@ -0,0 +1,24 @@ +// teaching - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const TeachingPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["create-learning-path","run-learning-retrospective"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default TeachingPlugin; diff --git a/thermos/.claude-plugin/plugin.json b/thermos/.claude-plugin/plugin.json new file mode 100644 index 00000000..de9e1aad --- /dev/null +++ b/thermos/.claude-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "thermos", + "version": "1.0.0", + "description": "Thermo-nuclear branch review: deep correctness and security audits plus harsh code-quality rubrics, parallel subagents, thermos orchestration, and optional take-the-wheel and FSD merge-ready flows.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/thermos/.codex-plugin/plugin.json b/thermos/.codex-plugin/plugin.json new file mode 100644 index 00000000..9f1b654e --- /dev/null +++ b/thermos/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "thermos", + "version": "1.0.0", + "description": "Thermo-nuclear branch review: deep correctness and security audits plus harsh code-quality rubrics, parallel subagents, thermos orchestration, and optional take-the-wheel and FSD merge-ready flows.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "skills": "./skills/" +} diff --git a/thermos/.grok-plugin/plugin.json b/thermos/.grok-plugin/plugin.json new file mode 100644 index 00000000..de9e1aad --- /dev/null +++ b/thermos/.grok-plugin/plugin.json @@ -0,0 +1,9 @@ +{ + "name": "thermos", + "version": "1.0.0", + "description": "Thermo-nuclear branch review: deep correctness and security audits plus harsh code-quality rubrics, parallel subagents, thermos orchestration, and optional take-the-wheel and FSD merge-ready flows.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + } +} diff --git a/thermos/opencode.js b/thermos/opencode.js new file mode 100644 index 00000000..a64d346f --- /dev/null +++ b/thermos/opencode.js @@ -0,0 +1,30 @@ +// thermos - OpenCode adapter for Cursor plugin +// Reads existing assets (skills/, agents/, rules/, hooks/) at runtime and registers via OpenCode plugin API. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); + +export const ThermosPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["thermo-nuclear-code-quality-review","thermo-nuclear-review","thermos"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + config.agent ??= {}; + const agentsDir = join(PLUGIN_DIR, "agents"); + for (const agentFile of ["thermo-nuclear-code-quality-review-subagent.md","thermo-nuclear-review-subagent.md"]) { + const name = agentFile.replace(/\.md$/, ""); + const prompt = await readFile(join(agentsDir, agentFile), "utf-8"); + config.agent[name] = { description: name, mode: "subagent", prompt, permission: { edit: "deny", bash: "ask" } }; + } + + }, + + + // No-op hooks for interface completeness + "tool.execute.before": async () => {}, +}); + +export default ThermosPlugin; diff --git a/third_party/ahrefs/opencode.js b/third_party/ahrefs/opencode.js new file mode 100644 index 00000000..5acc887e --- /dev/null +++ b/third_party/ahrefs/opencode.js @@ -0,0 +1,61 @@ +// ahrefs - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const AhrefsPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default AhrefsPlugin; diff --git a/third_party/amplemarket/.claude-plugin/plugin.json b/third_party/amplemarket/.claude-plugin/plugin.json new file mode 100644 index 00000000..d1c483ed --- /dev/null +++ b/third_party/amplemarket/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "amplemarket", + "version": "1.0.0", + "description": "Search people and companies, enrich leads, run sequences.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/amplemarket/.codex-plugin/plugin.json b/third_party/amplemarket/.codex-plugin/plugin.json new file mode 100644 index 00000000..d1c483ed --- /dev/null +++ b/third_party/amplemarket/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "amplemarket", + "version": "1.0.0", + "description": "Search people and companies, enrich leads, run sequences.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/amplemarket/.grok-plugin/plugin.json b/third_party/amplemarket/.grok-plugin/plugin.json new file mode 100644 index 00000000..d1c483ed --- /dev/null +++ b/third_party/amplemarket/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "amplemarket", + "version": "1.0.0", + "description": "Search people and companies, enrich leads, run sequences.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/amplemarket/opencode.js b/third_party/amplemarket/opencode.js new file mode 100644 index 00000000..c4755f0c --- /dev/null +++ b/third_party/amplemarket/opencode.js @@ -0,0 +1,61 @@ +// amplemarket - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const AmplemarketPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default AmplemarketPlugin; diff --git a/third_party/ashby/.claude-plugin/plugin.json b/third_party/ashby/.claude-plugin/plugin.json new file mode 100644 index 00000000..06a5c6d0 --- /dev/null +++ b/third_party/ashby/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "ashby", + "version": "1.0.0", + "description": "Search candidates, prep interviews, and manage pipeline tasks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/ashby/.codex-plugin/plugin.json b/third_party/ashby/.codex-plugin/plugin.json new file mode 100644 index 00000000..06a5c6d0 --- /dev/null +++ b/third_party/ashby/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "ashby", + "version": "1.0.0", + "description": "Search candidates, prep interviews, and manage pipeline tasks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/ashby/.grok-plugin/plugin.json b/third_party/ashby/.grok-plugin/plugin.json new file mode 100644 index 00000000..06a5c6d0 --- /dev/null +++ b/third_party/ashby/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "ashby", + "version": "1.0.0", + "description": "Search candidates, prep interviews, and manage pipeline tasks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/ashby/opencode.js b/third_party/ashby/opencode.js new file mode 100644 index 00000000..31397c2a --- /dev/null +++ b/third_party/ashby/opencode.js @@ -0,0 +1,61 @@ +// ashby - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const AshbyPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default AshbyPlugin; diff --git a/third_party/brevo/opencode.js b/third_party/brevo/opencode.js new file mode 100644 index 00000000..ff58800c --- /dev/null +++ b/third_party/brevo/opencode.js @@ -0,0 +1,61 @@ +// brevo - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const BrevoPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default BrevoPlugin; diff --git a/third_party/brex/opencode.js b/third_party/brex/opencode.js new file mode 100644 index 00000000..26310606 --- /dev/null +++ b/third_party/brex/opencode.js @@ -0,0 +1,61 @@ +// brex - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const BrexPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default BrexPlugin; diff --git a/third_party/calendly/opencode.js b/third_party/calendly/opencode.js new file mode 100644 index 00000000..57d0911e --- /dev/null +++ b/third_party/calendly/opencode.js @@ -0,0 +1,61 @@ +// calendly - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const CalendlyPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default CalendlyPlugin; diff --git a/third_party/circleback/.claude-plugin/plugin.json b/third_party/circleback/.claude-plugin/plugin.json new file mode 100644 index 00000000..75374355 --- /dev/null +++ b/third_party/circleback/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "circleback", + "version": "1.0.0", + "description": "Search meetings, transcripts, action items, and emails.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/circleback/.codex-plugin/plugin.json b/third_party/circleback/.codex-plugin/plugin.json new file mode 100644 index 00000000..75374355 --- /dev/null +++ b/third_party/circleback/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "circleback", + "version": "1.0.0", + "description": "Search meetings, transcripts, action items, and emails.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/circleback/.grok-plugin/plugin.json b/third_party/circleback/.grok-plugin/plugin.json new file mode 100644 index 00000000..75374355 --- /dev/null +++ b/third_party/circleback/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "circleback", + "version": "1.0.0", + "description": "Search meetings, transcripts, action items, and emails.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/circleback/opencode.js b/third_party/circleback/opencode.js new file mode 100644 index 00000000..b3959186 --- /dev/null +++ b/third_party/circleback/opencode.js @@ -0,0 +1,61 @@ +// circleback - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const CirclebackPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default CirclebackPlugin; diff --git a/third_party/clay/.claude-plugin/plugin.json b/third_party/clay/.claude-plugin/plugin.json new file mode 100644 index 00000000..fc50969d --- /dev/null +++ b/third_party/clay/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "clay", + "version": "1.0.0", + "description": "Enrich people and companies, run AI research agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/clay/.codex-plugin/plugin.json b/third_party/clay/.codex-plugin/plugin.json new file mode 100644 index 00000000..fc50969d --- /dev/null +++ b/third_party/clay/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "clay", + "version": "1.0.0", + "description": "Enrich people and companies, run AI research agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/clay/.grok-plugin/plugin.json b/third_party/clay/.grok-plugin/plugin.json new file mode 100644 index 00000000..fc50969d --- /dev/null +++ b/third_party/clay/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "clay", + "version": "1.0.0", + "description": "Enrich people and companies, run AI research agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/clay/opencode.js b/third_party/clay/opencode.js new file mode 100644 index 00000000..121a9ae3 --- /dev/null +++ b/third_party/clay/opencode.js @@ -0,0 +1,61 @@ +// clay - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const ClayPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default ClayPlugin; diff --git a/third_party/coda/opencode.js b/third_party/coda/opencode.js new file mode 100644 index 00000000..be1b7d6e --- /dev/null +++ b/third_party/coda/opencode.js @@ -0,0 +1,61 @@ +// coda - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const CodaPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default CodaPlugin; diff --git a/third_party/craft/opencode.js b/third_party/craft/opencode.js new file mode 100644 index 00000000..98c04ac5 --- /dev/null +++ b/third_party/craft/opencode.js @@ -0,0 +1,61 @@ +// craft - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const CraftPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default CraftPlugin; diff --git a/third_party/customer-io/opencode.js b/third_party/customer-io/opencode.js new file mode 100644 index 00000000..ded5300f --- /dev/null +++ b/third_party/customer-io/opencode.js @@ -0,0 +1,61 @@ +// customer-io - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const CustomerIoPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default CustomerIoPlugin; diff --git a/third_party/docusign/.claude-plugin/plugin.json b/third_party/docusign/.claude-plugin/plugin.json new file mode 100644 index 00000000..2ebbb8a5 --- /dev/null +++ b/third_party/docusign/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "docusign", + "version": "1.0.0", + "description": "Manage envelopes, templates, workflows, and agreements.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/docusign/.codex-plugin/plugin.json b/third_party/docusign/.codex-plugin/plugin.json new file mode 100644 index 00000000..2ebbb8a5 --- /dev/null +++ b/third_party/docusign/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "docusign", + "version": "1.0.0", + "description": "Manage envelopes, templates, workflows, and agreements.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/docusign/.grok-plugin/plugin.json b/third_party/docusign/.grok-plugin/plugin.json new file mode 100644 index 00000000..2ebbb8a5 --- /dev/null +++ b/third_party/docusign/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "docusign", + "version": "1.0.0", + "description": "Manage envelopes, templates, workflows, and agreements.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/docusign/opencode.js b/third_party/docusign/opencode.js new file mode 100644 index 00000000..42a3e831 --- /dev/null +++ b/third_party/docusign/opencode.js @@ -0,0 +1,61 @@ +// docusign - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const DocusignPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default DocusignPlugin; diff --git a/third_party/fathom/opencode.js b/third_party/fathom/opencode.js new file mode 100644 index 00000000..a795da42 --- /dev/null +++ b/third_party/fathom/opencode.js @@ -0,0 +1,61 @@ +// fathom - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const FathomPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default FathomPlugin; diff --git a/third_party/fireflies/opencode.js b/third_party/fireflies/opencode.js new file mode 100644 index 00000000..a7358700 --- /dev/null +++ b/third_party/fireflies/opencode.js @@ -0,0 +1,61 @@ +// fireflies - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const FirefliesPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default FirefliesPlugin; diff --git a/third_party/github/.claude-plugin/plugin.json b/third_party/github/.claude-plugin/plugin.json new file mode 100644 index 00000000..1ded4bfa --- /dev/null +++ b/third_party/github/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "github", + "version": "1.0.0", + "description": "Manage repos, issues, pull requests, and Actions.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/github/.codex-plugin/plugin.json b/third_party/github/.codex-plugin/plugin.json new file mode 100644 index 00000000..1ded4bfa --- /dev/null +++ b/third_party/github/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "github", + "version": "1.0.0", + "description": "Manage repos, issues, pull requests, and Actions.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/github/.grok-plugin/plugin.json b/third_party/github/.grok-plugin/plugin.json new file mode 100644 index 00000000..1ded4bfa --- /dev/null +++ b/third_party/github/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "github", + "version": "1.0.0", + "description": "Manage repos, issues, pull requests, and Actions.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/github/opencode.js b/third_party/github/opencode.js new file mode 100644 index 00000000..eaf0ffd1 --- /dev/null +++ b/third_party/github/opencode.js @@ -0,0 +1,61 @@ +// github - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GithubPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GithubPlugin; diff --git a/third_party/gmail/.claude-plugin/plugin.json b/third_party/gmail/.claude-plugin/plugin.json new file mode 100644 index 00000000..c9c3f538 --- /dev/null +++ b/third_party/gmail/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gmail", + "version": "1.0.0", + "description": "Search, read, draft, and manage email.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gmail/.codex-plugin/plugin.json b/third_party/gmail/.codex-plugin/plugin.json new file mode 100644 index 00000000..c9c3f538 --- /dev/null +++ b/third_party/gmail/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gmail", + "version": "1.0.0", + "description": "Search, read, draft, and manage email.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gmail/.grok-plugin/plugin.json b/third_party/gmail/.grok-plugin/plugin.json new file mode 100644 index 00000000..c9c3f538 --- /dev/null +++ b/third_party/gmail/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gmail", + "version": "1.0.0", + "description": "Search, read, draft, and manage email.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gmail/opencode.js b/third_party/gmail/opencode.js new file mode 100644 index 00000000..3b61a14d --- /dev/null +++ b/third_party/gmail/opencode.js @@ -0,0 +1,61 @@ +// gmail - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GmailPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GmailPlugin; diff --git a/third_party/godaddy/opencode.js b/third_party/godaddy/opencode.js new file mode 100644 index 00000000..8ebc186f --- /dev/null +++ b/third_party/godaddy/opencode.js @@ -0,0 +1,61 @@ +// godaddy - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GodaddyPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GodaddyPlugin; diff --git a/third_party/gong/.claude-plugin/plugin.json b/third_party/gong/.claude-plugin/plugin.json new file mode 100644 index 00000000..59c9eeae --- /dev/null +++ b/third_party/gong/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gong", + "version": "1.0.0", + "description": "Pull account summaries, deal insights, and call briefs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gong/.codex-plugin/plugin.json b/third_party/gong/.codex-plugin/plugin.json new file mode 100644 index 00000000..59c9eeae --- /dev/null +++ b/third_party/gong/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gong", + "version": "1.0.0", + "description": "Pull account summaries, deal insights, and call briefs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gong/.grok-plugin/plugin.json b/third_party/gong/.grok-plugin/plugin.json new file mode 100644 index 00000000..59c9eeae --- /dev/null +++ b/third_party/gong/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "gong", + "version": "1.0.0", + "description": "Pull account summaries, deal insights, and call briefs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/gong/opencode.js b/third_party/gong/opencode.js new file mode 100644 index 00000000..304e34a8 --- /dev/null +++ b/third_party/gong/opencode.js @@ -0,0 +1,61 @@ +// gong - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GongPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GongPlugin; diff --git a/third_party/google-calendar/.claude-plugin/plugin.json b/third_party/google-calendar/.claude-plugin/plugin.json new file mode 100644 index 00000000..1650161c --- /dev/null +++ b/third_party/google-calendar/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-calendar", + "version": "1.0.0", + "description": "Search events and schedule meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-calendar/.codex-plugin/plugin.json b/third_party/google-calendar/.codex-plugin/plugin.json new file mode 100644 index 00000000..1650161c --- /dev/null +++ b/third_party/google-calendar/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-calendar", + "version": "1.0.0", + "description": "Search events and schedule meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-calendar/.grok-plugin/plugin.json b/third_party/google-calendar/.grok-plugin/plugin.json new file mode 100644 index 00000000..1650161c --- /dev/null +++ b/third_party/google-calendar/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-calendar", + "version": "1.0.0", + "description": "Search events and schedule meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-calendar/opencode.js b/third_party/google-calendar/opencode.js new file mode 100644 index 00000000..9ed43240 --- /dev/null +++ b/third_party/google-calendar/opencode.js @@ -0,0 +1,61 @@ +// google-calendar - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GoogleCalendarPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GoogleCalendarPlugin; diff --git a/third_party/google-drive/.claude-plugin/plugin.json b/third_party/google-drive/.claude-plugin/plugin.json new file mode 100644 index 00000000..bdbd367a --- /dev/null +++ b/third_party/google-drive/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-drive", + "version": "1.0.0", + "description": "Search, read, create, and share files.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-drive/.codex-plugin/plugin.json b/third_party/google-drive/.codex-plugin/plugin.json new file mode 100644 index 00000000..bdbd367a --- /dev/null +++ b/third_party/google-drive/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-drive", + "version": "1.0.0", + "description": "Search, read, create, and share files.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-drive/.grok-plugin/plugin.json b/third_party/google-drive/.grok-plugin/plugin.json new file mode 100644 index 00000000..bdbd367a --- /dev/null +++ b/third_party/google-drive/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "google-drive", + "version": "1.0.0", + "description": "Search, read, create, and share files.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/google-drive/opencode.js b/third_party/google-drive/opencode.js new file mode 100644 index 00000000..42776428 --- /dev/null +++ b/third_party/google-drive/opencode.js @@ -0,0 +1,61 @@ +// google-drive - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GoogleDrivePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GoogleDrivePlugin; diff --git a/third_party/guru/opencode.js b/third_party/guru/opencode.js new file mode 100644 index 00000000..7d9dc6da --- /dev/null +++ b/third_party/guru/opencode.js @@ -0,0 +1,61 @@ +// guru - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const GuruPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default GuruPlugin; diff --git a/third_party/hubspot/.claude-plugin/plugin.json b/third_party/hubspot/.claude-plugin/plugin.json new file mode 100644 index 00000000..a92514b4 --- /dev/null +++ b/third_party/hubspot/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "hubspot", + "version": "1.0.0", + "description": "Search and update contacts, companies, deals, and tickets.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/hubspot/.codex-plugin/plugin.json b/third_party/hubspot/.codex-plugin/plugin.json new file mode 100644 index 00000000..a92514b4 --- /dev/null +++ b/third_party/hubspot/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "hubspot", + "version": "1.0.0", + "description": "Search and update contacts, companies, deals, and tickets.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/hubspot/.grok-plugin/plugin.json b/third_party/hubspot/.grok-plugin/plugin.json new file mode 100644 index 00000000..a92514b4 --- /dev/null +++ b/third_party/hubspot/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "hubspot", + "version": "1.0.0", + "description": "Search and update contacts, companies, deals, and tickets.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/hubspot/opencode.js b/third_party/hubspot/opencode.js new file mode 100644 index 00000000..03c72b3f --- /dev/null +++ b/third_party/hubspot/opencode.js @@ -0,0 +1,61 @@ +// hubspot - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const HubspotPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default HubspotPlugin; diff --git a/third_party/intercom/.claude-plugin/plugin.json b/third_party/intercom/.claude-plugin/plugin.json new file mode 100644 index 00000000..178f5a59 --- /dev/null +++ b/third_party/intercom/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "intercom", + "version": "1.0.0", + "description": "Search conversations, contacts, and Help Center articles.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/intercom/.codex-plugin/plugin.json b/third_party/intercom/.codex-plugin/plugin.json new file mode 100644 index 00000000..178f5a59 --- /dev/null +++ b/third_party/intercom/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "intercom", + "version": "1.0.0", + "description": "Search conversations, contacts, and Help Center articles.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/intercom/.grok-plugin/plugin.json b/third_party/intercom/.grok-plugin/plugin.json new file mode 100644 index 00000000..178f5a59 --- /dev/null +++ b/third_party/intercom/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "intercom", + "version": "1.0.0", + "description": "Search conversations, contacts, and Help Center articles.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/intercom/opencode.js b/third_party/intercom/opencode.js new file mode 100644 index 00000000..ed7cf62e --- /dev/null +++ b/third_party/intercom/opencode.js @@ -0,0 +1,61 @@ +// intercom - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const IntercomPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default IntercomPlugin; diff --git a/third_party/jotform/opencode.js b/third_party/jotform/opencode.js new file mode 100644 index 00000000..fd5edd67 --- /dev/null +++ b/third_party/jotform/opencode.js @@ -0,0 +1,61 @@ +// jotform - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const JotformPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default JotformPlugin; diff --git a/third_party/juicebox/.claude-plugin/plugin.json b/third_party/juicebox/.claude-plugin/plugin.json new file mode 100644 index 00000000..a074d11e --- /dev/null +++ b/third_party/juicebox/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "juicebox", + "version": "1.0.0", + "description": "Query recruiting analytics, shortlists, and sourcing agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/juicebox/.codex-plugin/plugin.json b/third_party/juicebox/.codex-plugin/plugin.json new file mode 100644 index 00000000..a074d11e --- /dev/null +++ b/third_party/juicebox/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "juicebox", + "version": "1.0.0", + "description": "Query recruiting analytics, shortlists, and sourcing agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/juicebox/.grok-plugin/plugin.json b/third_party/juicebox/.grok-plugin/plugin.json new file mode 100644 index 00000000..a074d11e --- /dev/null +++ b/third_party/juicebox/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "juicebox", + "version": "1.0.0", + "description": "Query recruiting analytics, shortlists, and sourcing agents.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/juicebox/opencode.js b/third_party/juicebox/opencode.js new file mode 100644 index 00000000..473abddd --- /dev/null +++ b/third_party/juicebox/opencode.js @@ -0,0 +1,61 @@ +// juicebox - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const JuiceboxPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default JuiceboxPlugin; diff --git a/third_party/klaviyo/opencode.js b/third_party/klaviyo/opencode.js new file mode 100644 index 00000000..b805bd9d --- /dev/null +++ b/third_party/klaviyo/opencode.js @@ -0,0 +1,61 @@ +// klaviyo - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const KlaviyoPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default KlaviyoPlugin; diff --git a/third_party/mailerlite/opencode.js b/third_party/mailerlite/opencode.js new file mode 100644 index 00000000..fd73a7c7 --- /dev/null +++ b/third_party/mailerlite/opencode.js @@ -0,0 +1,61 @@ +// mailerlite - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const MailerlitePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default MailerlitePlugin; diff --git a/third_party/mem/opencode.js b/third_party/mem/opencode.js new file mode 100644 index 00000000..e5203919 --- /dev/null +++ b/third_party/mem/opencode.js @@ -0,0 +1,61 @@ +// mem - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const MemPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default MemPlugin; diff --git a/third_party/mercury/opencode.js b/third_party/mercury/opencode.js new file mode 100644 index 00000000..29e9f975 --- /dev/null +++ b/third_party/mercury/opencode.js @@ -0,0 +1,61 @@ +// mercury - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const MercuryPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default MercuryPlugin; diff --git a/third_party/navan/.claude-plugin/plugin.json b/third_party/navan/.claude-plugin/plugin.json new file mode 100644 index 00000000..f757d56e --- /dev/null +++ b/third_party/navan/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "navan", + "version": "1.0.0", + "description": "Query expenses, travel bookings, policies, and cards.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/navan/.codex-plugin/plugin.json b/third_party/navan/.codex-plugin/plugin.json new file mode 100644 index 00000000..f757d56e --- /dev/null +++ b/third_party/navan/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "navan", + "version": "1.0.0", + "description": "Query expenses, travel bookings, policies, and cards.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/navan/.grok-plugin/plugin.json b/third_party/navan/.grok-plugin/plugin.json new file mode 100644 index 00000000..f757d56e --- /dev/null +++ b/third_party/navan/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "navan", + "version": "1.0.0", + "description": "Query expenses, travel bookings, policies, and cards.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/navan/opencode.js b/third_party/navan/opencode.js new file mode 100644 index 00000000..b6f1c751 --- /dev/null +++ b/third_party/navan/opencode.js @@ -0,0 +1,61 @@ +// navan - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const NavanPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default NavanPlugin; diff --git a/third_party/onedrive/opencode.js b/third_party/onedrive/opencode.js new file mode 100644 index 00000000..0f964ea1 --- /dev/null +++ b/third_party/onedrive/opencode.js @@ -0,0 +1,61 @@ +// onedrive - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const OnedrivePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default OnedrivePlugin; diff --git a/third_party/otter/opencode.js b/third_party/otter/opencode.js new file mode 100644 index 00000000..a1868ea1 --- /dev/null +++ b/third_party/otter/opencode.js @@ -0,0 +1,61 @@ +// otter - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const OtterPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default OtterPlugin; diff --git a/third_party/outlook-calendar/opencode.js b/third_party/outlook-calendar/opencode.js new file mode 100644 index 00000000..26cb5604 --- /dev/null +++ b/third_party/outlook-calendar/opencode.js @@ -0,0 +1,61 @@ +// outlook-calendar - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const OutlookCalendarPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default OutlookCalendarPlugin; diff --git a/third_party/outlook/opencode.js b/third_party/outlook/opencode.js new file mode 100644 index 00000000..90eb1508 --- /dev/null +++ b/third_party/outlook/opencode.js @@ -0,0 +1,61 @@ +// outlook - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const OutlookPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default OutlookPlugin; diff --git a/third_party/outreach/.claude-plugin/plugin.json b/third_party/outreach/.claude-plugin/plugin.json new file mode 100644 index 00000000..b4156585 --- /dev/null +++ b/third_party/outreach/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "outreach", + "version": "1.0.0", + "description": "Search sequences, prospects, and Kaia meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/outreach/.codex-plugin/plugin.json b/third_party/outreach/.codex-plugin/plugin.json new file mode 100644 index 00000000..b4156585 --- /dev/null +++ b/third_party/outreach/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "outreach", + "version": "1.0.0", + "description": "Search sequences, prospects, and Kaia meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/outreach/.grok-plugin/plugin.json b/third_party/outreach/.grok-plugin/plugin.json new file mode 100644 index 00000000..b4156585 --- /dev/null +++ b/third_party/outreach/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "outreach", + "version": "1.0.0", + "description": "Search sequences, prospects, and Kaia meetings.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/outreach/opencode.js b/third_party/outreach/opencode.js new file mode 100644 index 00000000..7c90c1b1 --- /dev/null +++ b/third_party/outreach/opencode.js @@ -0,0 +1,61 @@ +// outreach - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const OutreachPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default OutreachPlugin; diff --git a/third_party/playwright/.claude-plugin/plugin.json b/third_party/playwright/.claude-plugin/plugin.json new file mode 100644 index 00000000..9605405b --- /dev/null +++ b/third_party/playwright/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "playwright", + "version": "1.0.0", + "description": "Navigate, click, screenshot, and test in a real browser.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/playwright/.codex-plugin/plugin.json b/third_party/playwright/.codex-plugin/plugin.json new file mode 100644 index 00000000..9605405b --- /dev/null +++ b/third_party/playwright/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "playwright", + "version": "1.0.0", + "description": "Navigate, click, screenshot, and test in a real browser.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/playwright/.grok-plugin/plugin.json b/third_party/playwright/.grok-plugin/plugin.json new file mode 100644 index 00000000..9605405b --- /dev/null +++ b/third_party/playwright/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "playwright", + "version": "1.0.0", + "description": "Navigate, click, screenshot, and test in a real browser.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/playwright/opencode.js b/third_party/playwright/opencode.js new file mode 100644 index 00000000..1fe14882 --- /dev/null +++ b/third_party/playwright/opencode.js @@ -0,0 +1,61 @@ +// playwright - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const PlaywrightPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default PlaywrightPlugin; diff --git a/third_party/profound/.claude-plugin/plugin.json b/third_party/profound/.claude-plugin/plugin.json new file mode 100644 index 00000000..c2334d45 --- /dev/null +++ b/third_party/profound/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "profound", + "version": "1.0.0", + "description": "Track AI visibility, sentiment, and citations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/profound/.codex-plugin/plugin.json b/third_party/profound/.codex-plugin/plugin.json new file mode 100644 index 00000000..c2334d45 --- /dev/null +++ b/third_party/profound/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "profound", + "version": "1.0.0", + "description": "Track AI visibility, sentiment, and citations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/profound/.grok-plugin/plugin.json b/third_party/profound/.grok-plugin/plugin.json new file mode 100644 index 00000000..c2334d45 --- /dev/null +++ b/third_party/profound/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "profound", + "version": "1.0.0", + "description": "Track AI visibility, sentiment, and citations.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/profound/opencode.js b/third_party/profound/opencode.js new file mode 100644 index 00000000..fd879a90 --- /dev/null +++ b/third_party/profound/opencode.js @@ -0,0 +1,61 @@ +// profound - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const ProfoundPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default ProfoundPlugin; diff --git a/third_party/readwise/opencode.js b/third_party/readwise/opencode.js new file mode 100644 index 00000000..a70553f5 --- /dev/null +++ b/third_party/readwise/opencode.js @@ -0,0 +1,61 @@ +// readwise - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const ReadwisePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default ReadwisePlugin; diff --git a/third_party/salesforce/.claude-plugin/plugin.json b/third_party/salesforce/.claude-plugin/plugin.json new file mode 100644 index 00000000..19fd304f --- /dev/null +++ b/third_party/salesforce/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "salesforce", + "version": "1.0.0", + "description": "Query, create, and update records in your org.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/salesforce/.codex-plugin/plugin.json b/third_party/salesforce/.codex-plugin/plugin.json new file mode 100644 index 00000000..19fd304f --- /dev/null +++ b/third_party/salesforce/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "salesforce", + "version": "1.0.0", + "description": "Query, create, and update records in your org.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/salesforce/.grok-plugin/plugin.json b/third_party/salesforce/.grok-plugin/plugin.json new file mode 100644 index 00000000..19fd304f --- /dev/null +++ b/third_party/salesforce/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "salesforce", + "version": "1.0.0", + "description": "Query, create, and update records in your org.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/salesforce/opencode.js b/third_party/salesforce/opencode.js new file mode 100644 index 00000000..fb87d6f9 --- /dev/null +++ b/third_party/salesforce/opencode.js @@ -0,0 +1,61 @@ +// salesforce - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const SalesforcePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default SalesforcePlugin; diff --git a/third_party/semrush/opencode.js b/third_party/semrush/opencode.js new file mode 100644 index 00000000..80190122 --- /dev/null +++ b/third_party/semrush/opencode.js @@ -0,0 +1,61 @@ +// semrush - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const SemrushPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default SemrushPlugin; diff --git a/third_party/similarweb/opencode.js b/third_party/similarweb/opencode.js new file mode 100644 index 00000000..e4075ac9 --- /dev/null +++ b/third_party/similarweb/opencode.js @@ -0,0 +1,61 @@ +// similarweb - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const SimilarwebPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default SimilarwebPlugin; diff --git a/third_party/smartsheet/opencode.js b/third_party/smartsheet/opencode.js new file mode 100644 index 00000000..c1fd11d3 --- /dev/null +++ b/third_party/smartsheet/opencode.js @@ -0,0 +1,61 @@ +// smartsheet - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const SmartsheetPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default SmartsheetPlugin; diff --git a/third_party/todoist/opencode.js b/third_party/todoist/opencode.js new file mode 100644 index 00000000..4172ad4c --- /dev/null +++ b/third_party/todoist/opencode.js @@ -0,0 +1,61 @@ +// todoist - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const TodoistPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default TodoistPlugin; diff --git a/third_party/typeform/opencode.js b/third_party/typeform/opencode.js new file mode 100644 index 00000000..4aaa3a57 --- /dev/null +++ b/third_party/typeform/opencode.js @@ -0,0 +1,61 @@ +// typeform - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const TypeformPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default TypeformPlugin; diff --git a/third_party/upwork/opencode.js b/third_party/upwork/opencode.js new file mode 100644 index 00000000..5b46712f --- /dev/null +++ b/third_party/upwork/opencode.js @@ -0,0 +1,61 @@ +// upwork - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const UpworkPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default UpworkPlugin; diff --git a/third_party/workable/opencode.js b/third_party/workable/opencode.js new file mode 100644 index 00000000..7f7bbe55 --- /dev/null +++ b/third_party/workable/opencode.js @@ -0,0 +1,61 @@ +// workable - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const WorkablePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default WorkablePlugin; diff --git a/third_party/wrike/opencode.js b/third_party/wrike/opencode.js new file mode 100644 index 00000000..70b569e8 --- /dev/null +++ b/third_party/wrike/opencode.js @@ -0,0 +1,61 @@ +// wrike - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const WrikePlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default WrikePlugin; diff --git a/third_party/x-ads/opencode.js b/third_party/x-ads/opencode.js new file mode 100644 index 00000000..0752e796 --- /dev/null +++ b/third_party/x-ads/opencode.js @@ -0,0 +1,61 @@ +// x-ads - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const XAdsPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default XAdsPlugin; diff --git a/third_party/x/.claude-plugin/plugin.json b/third_party/x/.claude-plugin/plugin.json new file mode 100644 index 00000000..bc458b22 --- /dev/null +++ b/third_party/x/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "x", + "version": "2.0.0", + "description": "Search posts, read timelines, pull trends, and manage bookmarks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/x/.codex-plugin/plugin.json b/third_party/x/.codex-plugin/plugin.json new file mode 100644 index 00000000..bc458b22 --- /dev/null +++ b/third_party/x/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "x", + "version": "2.0.0", + "description": "Search posts, read timelines, pull trends, and manage bookmarks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/x/.grok-plugin/plugin.json b/third_party/x/.grok-plugin/plugin.json new file mode 100644 index 00000000..bc458b22 --- /dev/null +++ b/third_party/x/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "x", + "version": "2.0.0", + "description": "Search posts, read timelines, pull trends, and manage bookmarks.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/x/opencode.js b/third_party/x/opencode.js new file mode 100644 index 00000000..a6835477 --- /dev/null +++ b/third_party/x/opencode.js @@ -0,0 +1,64 @@ +// x - OpenCode adapter for Cursor plugin (skills + MCP) +// Reads skills/ and mcp.json at runtime, registers both via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile, readdir } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const XPlugin = async ({ directory }) => ({ + config: async (config) => { + const skills = ["x-api-mcp-guide"]; + config.permission ??= { skill: {} }; + for (const s of skills) config.permission.skill[s] = "allow"; + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default XPlugin; diff --git a/third_party/xero/opencode.js b/third_party/xero/opencode.js new file mode 100644 index 00000000..c2323224 --- /dev/null +++ b/third_party/xero/opencode.js @@ -0,0 +1,61 @@ +// xero - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const XeroPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default XeroPlugin; diff --git a/third_party/zoom/.claude-plugin/plugin.json b/third_party/zoom/.claude-plugin/plugin.json new file mode 100644 index 00000000..7cdd5f6b --- /dev/null +++ b/third_party/zoom/.claude-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "zoom", + "version": "1.0.0", + "description": "Search meetings, pull transcripts, and work with Zoom Docs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/zoom/.codex-plugin/plugin.json b/third_party/zoom/.codex-plugin/plugin.json new file mode 100644 index 00000000..7cdd5f6b --- /dev/null +++ b/third_party/zoom/.codex-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "zoom", + "version": "1.0.0", + "description": "Search meetings, pull transcripts, and work with Zoom Docs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/zoom/.grok-plugin/plugin.json b/third_party/zoom/.grok-plugin/plugin.json new file mode 100644 index 00000000..7cdd5f6b --- /dev/null +++ b/third_party/zoom/.grok-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "zoom", + "version": "1.0.0", + "description": "Search meetings, pull transcripts, and work with Zoom Docs.", + "author": { + "name": "Cursor", + "email": "plugins@cursor.com" + }, + "mcpServers": "./mcp.json" +} diff --git a/third_party/zoom/opencode.js b/third_party/zoom/opencode.js new file mode 100644 index 00000000..abb2f167 --- /dev/null +++ b/third_party/zoom/opencode.js @@ -0,0 +1,61 @@ +// zoom - OpenCode adapter for Cursor plugin (MCP) +// Reads mcp.json at runtime and registers server via config hook. + +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +const PLUGIN_DIR = fileURLToPath(new URL(".", import.meta.url)); +const MCP_PATH = join(PLUGIN_DIR, "mcp.json"); + +function translateServer(server) { + const isLocal = server.command || server.type === "stdio"; + if (isLocal) { + const cmd = [server.command, ...(server.args || [])].filter(Boolean); + const out = { type: "local", command: cmd, enabled: true }; + if (server.env) { + const envRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.environment = Object.fromEntries( + Object.entries(server.env).map(([k, v]) => [k, String(v).replace(envRegex, "{env:$1}")]) + ); + } + if (server.cwd) out.cwd = server.cwd; + return out; + } + const out = { type: "remote", enabled: true }; + if (server.url) { + const urlRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.url = String(server.url).replace(urlRegex, "{env:$1}"); + } + if (server.headers) { + const hdrRegex = new RegExp("\\\\${([^}]+)}", "g"); + out.headers = Object.fromEntries( + Object.entries(server.headers).map(([k, v]) => [k, String(v).replace(hdrRegex, "{env:$1}")]) + ); + out.oauth = false; + } else if (server.auth) { + const oauth = {}; + const cid = server.auth.CLIENT_ID || server.auth.clientId; + const csec = server.auth.CLIENT_SECRET || server.auth.clientSecret; + const authRegex = new RegExp("\\\\${([^}]+)}", "g"); + if (cid) oauth.clientId = String(cid).replace(authRegex, "{env:$1}"); + if (csec) oauth.clientSecret = String(csec).replace(authRegex, "{env:$1}"); + const scopes = server.auth.scopes || server.auth.scope; + if (Array.isArray(scopes)) oauth.scope = scopes.join(" "); + else if (typeof scopes === "string") oauth.scope = scopes; + out.oauth = oauth; + } + return out; +} + +export const ZoomPlugin = async () => ({ + config: async (config) => { + const mcp = JSON.parse(await readFile(MCP_PATH, "utf-8")); + for (const [name, server] of Object.entries(mcp.mcpServers || {})) { + config.mcp ??= {}; + config.mcp[name] = translateServer(server); + } + }, +}); + +export default ZoomPlugin;