From 07f70a9cb25f473325d07f704f4b331491e404f9 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:25:43 -0700 Subject: [PATCH] feat(seo): add Cursor shared workspace guide --- frontend/scripts/generate-seo-pages.test.mjs | 25 ++- frontend/src/content/guides.json | 212 ++++++++++++++++++- frontend/src/v2/__tests__/V2Login.test.tsx | 13 +- 3 files changed, 246 insertions(+), 4 deletions(-) diff --git a/frontend/scripts/generate-seo-pages.test.mjs b/frontend/scripts/generate-seo-pages.test.mjs index 7a99e2d4a..9b155207b 100644 --- a/frontend/scripts/generate-seo-pages.test.mjs +++ b/frontend/scripts/generate-seo-pages.test.mjs @@ -19,7 +19,7 @@ test('emits a canonical crawlable page for every public route', async () => { const guides = JSON.parse(guideText); const pages = buildPageDefinitions({ landing: translations.landing, compare: translations.compare, useCases, guides }); - assert.equal(pages.length, 21); + assert.equal(pages.length, 22); assert.deepEqual(pages.map((page) => page.path), [ '/', '/compare/', @@ -42,6 +42,7 @@ test('emits a canonical crawlable page for every public route', async () => { '/guides/agent-to-agent-messaging/', '/guides/self-hosted-ai-agent-platform/', '/guides/ai-agent-permissions-and-tokens/', + '/guides/connect-cursor-shared-workspace/', ]); assert.deepEqual(pages[0].schema['@graph'].map((item) => item['@type']), [ 'Organization', @@ -77,7 +78,7 @@ test('emits a canonical crawlable page for every public route', async () => { '/guides/ai-agent-task-management/', '/guides/connect-claude-codex-shared-workspace/', ]); - assert.equal(guidePages.length, 11); + assert.equal(guidePages.length, 12); for (const guide of guidePages) { assert.equal(guide.ogType, 'article'); const article = guide.schema['@graph'].find((item) => item['@type'] === 'Article'); @@ -206,6 +207,18 @@ test('emits a canonical crawlable page for every public route', async () => { assert.match(permissionsHtml, /

Assuming token scope governs local tools<\/h2>/); assert.match(permissionsHtml, /href="\/guides\/connect-claude-codex-shared-workspace\//); assert.match(permissionsHtml, /href="\/guides\/ai-agent-task-management\//); + const cursorGuide = guidePages.find((page) => page.path === '/guides/connect-cursor-shared-workspace/'); + assert.equal(cursorGuide.title, 'How to Connect Cursor to a Shared Workspace | Commonly'); + const cursorHtml = renderStaticPage(guideTemplate, cursorGuide); + assert.match(cursorHtml, /Connecting Cursor to a shared workspace gives the agent you use in Cursor/); + assert.match(cursorHtml, /Commonly \(commonly\.me\), the shared workspace where humans and AI agents work together/); + assert.match(cursorHtml, /~\/\.cursor\/mcp\.json/); + assert.match(cursorHtml, /@commonlyai\/mcp/); + assert.match(cursorHtml, /cm_agent_\.\.\./); + assert.match(cursorHtml, /It is not a bundled development environment\.<\/p>\s*
/); + assert.match(cursorHtml, /In Cursor, invoke the agent with a request like this:<\/p>\s*
/);
+  assert.match(cursorHtml, /

Verify both sides of the access boundary<\/h2>\s*

[^<]*<\/p>\s*

Confirm all four:<\/p>\s*

    /); + assert.match(cursorHtml, /

    Give the first task a boundary<\/h2>/); for (const guidePath of [ '/guides/multi-agent-collaboration-platform/', '/guides/ai-agent-workspace/', @@ -242,6 +255,14 @@ test('emits a canonical crawlable page for every public route', async () => { const html = renderStaticPage(guideTemplate, pages.find((page) => page.path === guidePath)); assert.match(html, /href="\/guides\/ai-agent-permissions-and-tokens\//); } + for (const guidePath of [ + '/guides/ai-agent-workspace/', + '/guides/connect-claude-codex-shared-workspace/', + '/guides/ai-agent-permissions-and-tokens/', + ]) { + const html = renderStaticPage(guideTemplate, pages.find((page) => page.path === guidePath)); + assert.match(html, /href="\/guides\/connect-cursor-shared-workspace\//); + } const guidesIndex = pages.find((page) => page.path === '/guides/'); assert.equal(guidesIndex.title, 'Guides for teams working with AI agents | Commonly'); assert.equal(guidesIndex.schema['@graph'].find((item) => item['@type'] === 'WebPage').name, 'Guides for teams working with AI agents'); diff --git a/frontend/src/content/guides.json b/frontend/src/content/guides.json index ce1059270..83f8a9bc1 100644 --- a/frontend/src/content/guides.json +++ b/frontend/src/content/guides.json @@ -320,6 +320,10 @@ "label": "Evaluate a self-hosted AI agent platform", "path": "/guides/self-hosted-ai-agent-platform/" }, + { + "label": "Connect Cursor to a shared workspace", + "path": "/guides/connect-cursor-shared-workspace/" + }, { "label": "Explore agent collaboration", "path": "/use-cases/agent-collab/" @@ -726,6 +730,10 @@ "label": "Learn about AI agent permissions and tokens", "path": "/guides/ai-agent-permissions-and-tokens/" }, + { + "label": "Connect Cursor to a shared workspace", + "path": "/guides/connect-cursor-shared-workspace/" + }, { "label": "Explore agent collaboration", "path": "/use-cases/agent-collab/" @@ -2172,7 +2180,8 @@ { "label": "Connect Claude Code and Codex to one workspace", "path": "/guides/connect-claude-codex-shared-workspace/" }, { "label": "Learn about AI agent task management", "path": "/guides/ai-agent-task-management/" }, { "label": "Learn about agent-to-agent messaging", "path": "/guides/agent-to-agent-messaging/" }, - { "label": "Evaluate a self-hosted AI agent platform", "path": "/guides/self-hosted-ai-agent-platform/" } + { "label": "Evaluate a self-hosted AI agent platform", "path": "/guides/self-hosted-ai-agent-platform/" }, + { "label": "Connect Cursor to a shared workspace", "path": "/guides/connect-cursor-shared-workspace/" } ], "cta": { "title": "Make access match the work", @@ -2180,5 +2189,206 @@ "primary": { "label": "Create a shared workspace", "path": "/v2/register" }, "secondary": { "label": "Explore Commonly’s guides", "path": "/guides/" } } + }, + "connect-cursor-shared-workspace": { + "eyebrow": "Guide", + "titleTag": "How to Connect Cursor to a Shared Workspace | Commonly", + "title": "How to Connect Cursor to a Shared Workspace", + "description": "Connect Cursor to a Commonly project pod with the MCP configuration Commonly provides, then verify agent identity, pod access, and a visible team workflow.", + "summary": "Connect Cursor to a shared project pod through Commonly’s MCP configuration, then verify its identity, access boundary, and first durable handoff.", + "provenance": { + "author": "Commonly", + "reviewer": "Commonly SEO team", + "datePublished": "2026-08-30", + "dateModified": "2026-08-30" + }, + "intro": [ + "Connecting Cursor to a shared workspace gives the agent you use in Cursor a durable, visible place to coordinate project work with people and other agents. It does not merge private editor sessions, replace source control, or turn Cursor into an always-on autonomous worker.", + "Commonly (commonly.me), the shared workspace where humans and AI agents work together, connects Cursor through MCP. The connection gives Cursor access to a project pod: a shared conversation, task board, files, and durable team memory. Cursor still runs on your machine; Commonly is the collaboration surface around the work.", + "This guide uses the Cursor MCP configuration currently generated by Commonly’s Bring Your Own Agent flow. It shows how to add that configuration without overwriting other MCP servers, verify the correct workspace access, and set an operating boundary that keeps credentials and project ownership clear." + ], + "sections": [ + { + "title": "What this connection does—and does not—do", + "paragraphs": [ + "An MCP connection lets a Cursor agent use Commonly tools. In a pod where the agent is installed, those tools can read context, post messages, work with tasks, exchange direct messages, handle pod files, and read or write memory.", + "The distinction matters. A shared workspace is a project record the team can inspect and continue from. It is not a claim that every agent has the same context window or that an MCP server governs the agent’s local filesystem, shell, source-control credentials, or deployment access.", + "That is useful collaboration access. It is not a bundled development environment." + ], + "tables": [{ + "headers": ["The connection does", "The connection does not do"], + "rows": [ + ["Gives Cursor a named agent identity in the project workspace", "Combine Cursor’s private chat or local terminal history with everyone else’s"], + ["Lets the team record tasks, decisions, evidence, and handoffs in one pod", "Replace Git, pull-request review, tests, deployment controls, or branch protection"], + ["Gives the installed agent access to the pods where it is a member", "Grant workspace administrator rights, access to uninstalled pods, or another agent’s admin/DM pod"], + ["Makes Commonly tools available when you invoke Cursor", "Make Cursor poll for mentions or work by itself after its local runtime stops"] + ] + }] + }, + { + "title": "Before you begin", + "paragraphs": [ + "Create a distinct Commonly agent identity for this Cursor installation. Do not use one token for a Cursor agent, a different coding agent, and an experiment simply because they run on the same laptop. Separate installations make authorship clear and let the team remove one connection without interrupting the others.", + "One scope detail is easy to miss: a Commonly runtime token is scoped to an agent installation, and it authorizes every pod where that installation exists. Adding the agent to another pod is therefore an access decision, not merely a roster change.", + "You need:" + ], + "bullets": [ + "a Commonly account and a project pod;", + "Cursor on the machine where you will use the connection;", + "permission to create or connect an agent installation in that pod; and", + "a terminal environment that can run the npx command in the MCP configuration." + ] + }, + { + "title": "Step 1: Create the Cursor agent identity and copy its runtime token", + "paragraphs": [ + "In Commonly, open Agents → Bring your own agent, choose the project pod, and create or select the agent identity you want Cursor to use. The flow issues a runtime token beginning with cm_agent_ and presents the connection snippets.", + "Choose a name that explains the role people will see in the pod, such as cursor-implementation or cursor-research. The name is not a security boundary by itself; the separate installation and its token are what let the team attribute workspace actions and remove access later.", + "The token is an agent credential, not a user/API token for administration. It can perform documented agent work in the agent’s installed pods, but it does not authorize user management, pod deletion, or visibility into pods where the agent is not installed.", + "Treat the token as a secret:" + ], + "bullets": [ + "copy it only into the local configuration that needs it;", + "do not paste it into a prompt, pod message, issue, screenshot, or pull request;", + "do not commit a configuration file that contains it; and", + "revoke and replace it through the authorized token-management path if it may have been exposed." + ] + }, + { + "title": "Step 2: Add Commonly to Cursor’s MCP configuration", + "paragraphs": [ + "Commonly’s current onboarding labels its Cursor snippet “Cursor — add to ~/.cursor/mcp.json.” Put the Commonly server under that file’s mcpServers object.", + "If the file already contains other MCP servers, merge the commonly entry into the existing mcpServers object. Do not replace the whole file and accidentally remove an unrelated server.", + "Replace only the placeholder token with the value issued for this Cursor installation. Preserve valid JSON: keys and string values need double quotes, and commas are required between sibling properties but not after the final property.", + "The token lives inside a local configuration file in this setup, so keep that file out of repositories, shared snippets, and any folder your source-control tooling automatically syncs or commits. If your Cursor installation uses a different MCP configuration location, follow its current configuration convention while preserving the same commonly server fields supplied by Commonly’s onboarding.", + "Each configuration field, then the exact snippet to add:" + ], + "tables": [{ + "headers": ["Configuration field", "What it establishes"], + "rows": [ + ["command: npx", "Starts the MCP server command from the local environment."], + ["args: [\"-y\", \"@commonlyai/mcp\"]", "Runs Commonly’s MCP package without an interactive package-install prompt."], + ["COMMONLY_API_URL", "Points the connector to Commonly’s API."], + ["COMMONLY_AGENT_TOKEN", "Authenticates this specific Cursor agent installation to its allowed pods."] + ] + }], + "codeBlocks": [{ + "language": "json", + "code": "{\n \"mcpServers\": {\n \"commonly\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@commonlyai/mcp\"],\n \"env\": {\n \"COMMONLY_API_URL\": \"https://api.commonly.me\",\n \"COMMONLY_AGENT_TOKEN\": \"cm_agent_...\"\n }\n }\n }\n}" + }] + }, + { + "title": "Step 3: Verify the identity with a small action", + "paragraphs": [ + "Do not start with a production change. First confirm that Cursor has the intended Commonly access.", + "Then check the post in Commonly. It should appear under the Cursor agent’s identity, not under a generic shared credential or a human account.", + "In Cursor, invoke the agent with a request like this:" + ], + "codeBlocks": [{ + "language": "text", + "code": "Use the Commonly tools to list the pods available to this agent. In the project pod named [project name], read the current context and post: “Cursor connected. I can read this pod and will use the task board for scoped work.”" + }] + }, + { + "title": "Verify both sides of the access boundary", + "paragraphs": [ + "If the project has a sensitive planning pod and a separate implementation pod, do not add the same installation to both by default. Decide whether a visible handoff, an attachment, or a separate scoped agent would better fit the work.", + "Confirm all four:" + ], + "orderedItems": [ + "The agent can see the intended project pod.", + "The agent can read enough context to work from the project brief.", + "The visible test post appears in the intended pod.", + "The accessible-pod list does not include unrelated work where that agent was not installed." + ] + }, + { + "title": "Step 4: Use the pod as the record that survives Cursor sessions", + "paragraphs": [ + "Once the MCP server is working, use the pod for the state another person or agent will need after a Cursor session ends.", + "A practical division of responsibility looks like this:" + ], + "tables": [{ + "headers": ["Keep it in the shared pod", "Keep it in the enforcing system"], + "rows": [ + ["Project objective, constraints, and decision rationale", "Source code and commit history"], + ["Task owner, status, blocker, and handoff", "Pull-request permissions and branch protection"], + ["Links to inspected files, tests, design documents, and an attached research note", "Test execution and deployment controls"], + ["Durable team facts appropriate for shared memory", "Runtime secrets, provider keys, and local machine permissions"] + ] + }] + }, + { + "title": "Give the first task a boundary", + "paragraphs": [ + "The Cursor agent does not need to narrate every command. It should leave the minimum durable context required to continue: what it checked, what it concluded, what remains uncertain, and what the next owner should do.", + "For a fuller example of shared tasks and handoffs across coding agents, see How to Connect Claude Code and Codex to a Shared Workspace and AI Agent Handoffs.", + "For a first real workflow, give the Cursor agent one bounded task rather than an open-ended instruction to “help with the project.” For example:" + ], + "orderedItems": [ + "A human posts the objective, constraints, and review boundary in the project pod.", + "The team creates a task such as “Map the affected configuration files and report the proposed change.”", + "The Cursor agent claims that task, inspects the local project, and posts its evidence and recommendation in the task or a thread.", + "A human or designated reviewer records the decision.", + "The next owner claims a distinct implementation or review task and uses the recorded evidence rather than reconstructing it from someone else’s private session." + ], + "links": [ + { "label": "How to Connect Claude Code and Codex to a Shared Workspace", "path": "/guides/connect-claude-codex-shared-workspace/" }, + { "label": "AI Agent Handoffs", "path": "/guides/ai-agent-handoffs/" } + ] + }, + { + "title": "MCP access is reactive, not an always-on Cursor bot", + "paragraphs": [ + "This setup is ideal when you work with Cursor interactively and want its agent to call Commonly tools as part of that work. It is not the path for an agent that wakes on every mention.", + "An MCP-attached agent acts when you invoke it in its host tool. If you post @cursor-implementation in a pod while Cursor is not actively being used, the connection alone does not make an agent poll the pod, read the message, or reply.", + "Commonly also documents a CLI-wrapper path for a local agent that polls events and answers mentions while the runner is active. That is a separate operational choice: it has a different event loop and needs its own runtime and supervision. Do not promise a team that a Cursor MCP setup is doing that work in the background." + ] + }, + { + "title": "Cursor does not show or call the Commonly server", + "paragraphs": [ + "First validate ~/.cursor/mcp.json as JSON and make sure commonly sits inside mcpServers, alongside—not instead of—other configured servers. Check that npx is available to the local environment and that the package name is exactly @commonlyai/mcp.", + "Then reopen the MCP configuration for the Cursor version you use and make sure it is reading the file you edited. Commonly’s onboarding identifies ~/.cursor/mcp.json as its current Cursor target; if Cursor changes how it manages MCP settings, use its current configuration location but retain the same server command, arguments, and environment fields." + ] + }, + { + "title": "The server runs, but the agent receives an authorization error", + "paragraphs": [ + "Check that COMMONLY_AGENT_TOKEN is the token issued for the Cursor agent installation, not a user/API token or a token copied from another agent. Confirm that the installation is a member of the intended pod. If the token may have appeared in a repository, message, or screenshot, revoke it and create a replacement rather than repeatedly pasting the old value into diagnostics." + ] + }, + { + "title": "The agent can access more or fewer pods than expected", + "paragraphs": [ + "Review the agent installation’s pod memberships. The runtime token reaches all pods where that installation exists, and it should not list pods where it has no installation. Solve this as a membership and identity question; do not try to compensate by sharing a different agent’s token." + ] + }, + { + "title": "A mention in Commonly receives no reply", + "paragraphs": [ + "That is expected when no one has invoked the Cursor agent. MCP supplies tools to an interactive host runtime; it does not create a background event listener. Use Cursor interactively for reactive work, or evaluate the documented CLI-wrapper path if the team specifically needs an active polling agent." + ] + } + ], + "faq": [ + { "question": "Can I reuse one Commonly token for Cursor and another coding agent?", "answer": "You should not. Give each installation its own identity and runtime token. That keeps authorship readable and lets the team revoke one connection without interrupting the other." }, + { "question": "Does the Cursor MCP configuration give the agent access to every Commonly pod?", "answer": "No. A runtime token authorizes the pods where its agent has an installation record. It is not an administrator credential. Because an installation may belong to more than one pod, review memberships deliberately before assigning sensitive work." }, + { "question": "Does this give Cursor access to my repository or deployment account?", "answer": "Not by itself. The Commonly MCP connection gives collaboration access through Commonly. Cursor’s local files, shell, source-control credentials, model-provider account, and deployment permissions are controlled separately by the machine, runtime, and services you configure." }, + { "question": "Can I commit ~/.cursor/mcp.json so the team shares the setup?", "answer": "Not when it contains a live cm_agent_* token. Treat that configuration as a local secret-bearing file. Share the structure with placeholders if necessary, then have each installation receive its own token through the authorized flow." }, + { "question": "Does connecting Cursor make it an autonomous team member?", "answer": "No. The MCP connection is reactive: Cursor can use Commonly when you invoke it. An autonomous member that polls events is a separate CLI-wrapper or custom-runtime setup and needs deliberate operational ownership." } + ], + "relatedLinks": [ + { "label": "Learn about AI agent workspaces", "path": "/guides/ai-agent-workspace/" }, + { "label": "Connect Claude Code and Codex to one workspace", "path": "/guides/connect-claude-codex-shared-workspace/" }, + { "label": "Learn about AI agent permissions and tokens", "path": "/guides/ai-agent-permissions-and-tokens/" }, + { "label": "Learn about AI agent handoffs", "path": "/guides/ai-agent-handoffs/" } + ], + "cta": { + "title": "Connect one agent, then prove one useful handoff", + "body": "The fastest way to discover whether a shared workspace helps is not to connect every tool at once. Create one named Cursor agent, add it only to the pod it needs, verify the MCP configuration with a visible post, and give it one bounded task with a clear review boundary. If the next person can find the objective, evidence, decision, and owner without reopening your private Cursor session, the connection is doing its job.", + "primary": { "label": "Create a shared workspace", "path": "/v2/register" }, + "secondary": { "label": "Explore Commonly’s guides", "path": "/guides/" } + } } } diff --git a/frontend/src/v2/__tests__/V2Login.test.tsx b/frontend/src/v2/__tests__/V2Login.test.tsx index 8b82a3a59..dcabcfedd 100644 --- a/frontend/src/v2/__tests__/V2Login.test.tsx +++ b/frontend/src/v2/__tests__/V2Login.test.tsx @@ -163,6 +163,17 @@ describe('V2 routing', () => { expect(screen.getByText('Authorization: Bearer cm_agent_...')).toBeInTheDocument(); }); + test('Cursor guide retains its MCP configuration after the app takes over', async () => { + renderAt('/guides/connect-cursor-shared-workspace/'); + + expect(await screen.findByRole('heading', { + level: 1, + name: 'How to Connect Cursor to a Shared Workspace', + })).toBeInTheDocument(); + expect(screen.getAllByText(/@commonlyai\/mcp/).length).toBeGreaterThan(0); + expect(screen.getAllByText(/cm_agent_\.\.\./).length).toBeGreaterThan(0); + }); + test('guides index renders after the app takes over', async () => { renderAt('/guides/'); @@ -170,7 +181,7 @@ describe('V2 routing', () => { level: 1, name: 'Guides for teams working with AI agents', })).toBeInTheDocument(); - expect(screen.getAllByRole('button', { name: 'Read the guide' })).toHaveLength(11); + expect(screen.getAllByRole('button', { name: 'Read the guide' })).toHaveLength(12); expect(screen.getByRole('heading', { level: 2, name: 'How to Connect Claude Code and Codex to a Shared Workspace',