> ## Documentation Index
> Fetch the complete documentation index at: https://subtext.fullstory.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Manually Install and Configure Subtext Step by Step

> Set up Subtext's capture snippet, agent plugin, and MCP server manually — ideal for custom frameworks, monorepos, or full control over the integration.

The Subtext setup wizard (`npx @subtextdev/subtext-wizard`) handles the full install in one step for most projects. If you are working with a custom framework, a monorepo with a non-standard structure, or simply prefer to control each piece of the integration yourself, this guide walks you through every step manually. You will add the capture snippet to your HTML, install the Subtext plugin for your agent (or configure the MCP server by hand), and end with a verification check to confirm everything is connected.

<Steps>
  <Step title="Add the capture snippet to your HTML">
    Open the HTML file that is served for every page of your application. Depending on your framework, this is typically:

    * **Create React App** — `public/index.html`
    * **Vite** — `index.html` at the project root
    * **Next.js App Router** — `app/layout.tsx` (use `<Script strategy="afterInteractive">` from `next/script`)
    * **Remix** — `app/root.tsx` (use `dangerouslySetInnerHTML` on a `<script>` tag)
    * **Custom or static HTML** — whichever `.html` file wraps your application shell

    Paste the snippet before the closing `</head>` tag (or equivalent). Replace `YOUR_ORG_ID` with the Org ID from **Subtext → Settings → Capture**.

    ```html theme={null}
    <script>
      window['_fs_host'] = 'fullstory.com';
      window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
      window['_fs_org'] = 'YOUR_ORG_ID';
      window['_fs_namespace'] = 'FS';
      !function(m,n,e,t,l,o,g,y){var s,f,a=function(h){return!(h in m)||(m.console&&m.console.log&&m.console.log('FullStory namespace conflict. More info: https://www.fullstory.com/developer/ns.html'),!1)};if(a(l)&&a(n)){m[l]={q:[]};var d=function(){return Array.prototype.slice.call(arguments,0)};m[l].identify=function(){m[l].q.push(["identify"].concat(d.apply(null,arguments)))};m[l].setUserVars=function(){m[l].q.push(["setUserVars"].concat(d.apply(null,arguments)))};m[l].event=function(){m[l].q.push(["event"].concat(d.apply(null,arguments)))};m[l].anonymize=function(){m[l].q.push(["anonymize"].concat(d.apply(null,arguments)))};m[l].shutdown=function(){m[l].q.push(["shutdown"].concat(d.apply(null,arguments)))};m[l].restart=function(){m[l].q.push(["restart"].concat(d.apply(null,arguments)))};m[l].log=function(a,b){m[l].q.push(["log",a,b])};m[l].consent=function(){m[l].q.push(["consent"].concat(d.apply(null,arguments)))};m[l].identifyAccount=function(){m[l].q.push(["identifyAccount"].concat(d.apply(null,arguments)))};m[l].clearUserCookie=function(){m[l].q.push(["clearUserCookie"].concat(d.apply(null,arguments)))};m[l].setVars=function(){m[l].q.push(["setVars"].concat(d.apply(null,arguments)))};m[l]._w={};y="XMLHttpRequest";m[l]._w[y]=m[y];y="fetch";m[l]._w[y]=m[y];if(m[y])m[l]._w[y]=m[y];m[l]._v="1.3.0";g=n.createElement(e);g.async=1;g.crossOrigin="anonymous";g.src='https://'+m[_fs_script];y=n.getElementsByTagName(e)[0];y.parentNode.insertBefore(g,y)}}(window,document,"script","_fs_config",n,o,g,y);
    </script>
    ```

    <Note>
      The snippet loads `fs.js` from Fullstory's edge asynchronously. It does not block rendering and adds no measurable overhead to page load time.
    </Note>
  </Step>

  <Step title="Install the Subtext plugin for your agent">
    The plugin is the fastest way to connect an agent: it configures the MCP server connection and installs the Subtext skills and commands in one step, and it updates automatically.

    **Claude Code** (full plugin — MCP server, skills, and commands):

    ```text theme={null}
    /plugin marketplace add fullstorydev/subtext
    /plugin install subtext@subtext-marketplace
    ```

    **Cursor** — install [Subtext from the Cursor marketplace](https://cursor.com/marketplace/subtext) directly inside Cursor.

    **Codex** — open `/plugins` and install **subtext** from the repo marketplace.

    **Gemini CLI**:

    ```bash theme={null}
    gemini extensions install https://github.com/fullstorydev/subtext
    ```

    **Windsurf and other editors** — install the skills via [OpenSkills](https://github.com/fullstorydev/subtext), then add the MCP server manually in the next step:

    ```bash theme={null}
    npx openskills install fullstorydev/subtext
    npx openskills sync
    ```

    **claude.ai** — Subtext is also available as a connector in the [Claude directory](https://claude.ai/directory/connectors/subtext).

    <Note>
      If you installed the plugin for Claude Code, Cursor, Codex, or Gemini CLI, the MCP server is configured for you — skip the next step and go straight to verification.
    </Note>
  </Step>

  <Step title="Add the MCP server manually (if needed)">
    If your tool didn't configure MCP servers automatically during installation, add the server by hand. Subtext's MCP server is hosted — `https://api.fullstory.com/mcp/subtext` over HTTP, with no local process to run.

    <Tabs>
      <Tab title="OAuth (recommended)">
        Add the server URL without any headers — your tool handles the OAuth flow automatically.

        <CodeGroup>
          ```json Claude Code (~/.claude/settings.json) theme={null}
          {
            "mcpServers": {
              "subtext": {
                "type": "http",
                "url": "https://api.fullstory.com/mcp/subtext"
              }
            }
          }
          ```

          ```json Cursor (Settings → MCP → Add Server) theme={null}
          {
            "subtext": {
              "type": "http",
              "url": "https://api.fullstory.com/mcp/subtext"
            }
          }
          ```
        </CodeGroup>
      </Tab>

      <Tab title="API key">
        Get your API key from [Settings → API Keys](https://app.fullstory.com/ui/org/settings/apikeys) and pass it as a Bearer token header.

        <CodeGroup>
          ```json Claude Code (~/.claude/settings.json) theme={null}
          {
            "mcpServers": {
              "subtext": {
                "type": "http",
                "url": "https://api.fullstory.com/mcp/subtext",
                "headers": {
                  "Authorization": "Bearer YOUR_SUBTEXT_API_KEY"
                }
              }
            }
          }
          ```

          ```json Cursor (Settings → MCP → Add Server) theme={null}
          {
            "subtext": {
              "type": "http",
              "url": "https://api.fullstory.com/mcp/subtext",
              "headers": {
                "Authorization": "Bearer YOUR_SUBTEXT_API_KEY"
              }
            }
          }
          ```
        </CodeGroup>

        <Warning>
          Keep your API key private. Do not commit it to source control. If you are working in a team environment, use environment variable injection or a secrets manager rather than hardcoding the key in a shared config file.
        </Warning>
      </Tab>
    </Tabs>

    <Note>
      Any tool that accepts a remote MCP URL — Linear's **Custom URL…** field, for example — takes the same endpoint directly.
    </Note>

    Once saved, restart your agent or reload its MCP configuration.
  </Step>

  <Step title="Verify the full integration">
    With the snippet deployed and your agent connected, run through this checklist to confirm everything is working end to end:

    **Capture is working:**

    1. Open your application in a browser and navigate to a few pages.
    2. Go to **Subtext → Sessions**. A new session should appear within 60 seconds.
    3. Click into the session and confirm you can see a replay timeline with page events.

    **MCP server is reachable:**

    1. Open your agent (Claude Code, Cursor, etc.) in the project where you installed the plugin or added the MCP config.
    2. Ask it: *"List my recent Subtext sessions."*
    3. The agent should respond with a list of sessions from your account. If it returns an authentication error, redo the OAuth flow (or check the Bearer token value), and confirm the config file is in the correct location for your agent.

    **Skills are available:**

    1. In your agent, type `/` and look for Subtext skills in the command list (e.g., `/subtext:review`, `/subtext:session`).
    2. If they do not appear, reinstall the plugin (or re-run `npx openskills sync`) and restart your agent.

    <Tip>
      If sessions appear in the Subtext dashboard but the MCP server cannot be reached, the most common cause is a misplaced or malformed MCP config file. Check that the JSON is valid (no trailing commas) and that the file path matches what your agent expects.
    </Tip>
  </Step>
</Steps>

<Note>
  The capture snippet records full production sessions and masks all form inputs by default. Once your agent is connected, it can review sessions, flag additional PII, and propose masking rules for your approval — closing the privacy governance loop without manual scrubbing.
</Note>
