# EzWebex for Codex

EzWebex is a Codex plugin for working with Webex messages, spaces, meetings, recordings, summaries, transcripts, and Atlassian Confluence knowledge.

The plugin runs a local MCP proxy inside Codex and uses a hosted CINT OAuth broker for Webex sign-in. It also configures a lazy wrapper for the official Atlassian Rovo remote MCP server through `mcp-remote` for Atlassian Cloud access and a local read-only Confluence Data Center MCP server for Cisco GPK2 pages. Users do not run a Webex auth broker locally.

## What It Does

- Reads, searches, creates, edits, and deletes Webex messages.
- Adds Webex app deeplinks and citation markdown to message results so briefs and summaries can cite the exact source message.
- Works with spaces, memberships, files, webhooks, and message threads.
- Lists, creates, updates, and deletes Webex meetings.
- Retrieves meeting status, participants, summaries, recordings, recording recaps, and transcripts.
- Prepares daily briefings from meetings, related spaces, DMs, mentions, transcripts, and outstanding requests.
- Builds private, user-scoped person profiles and "who should I ask?" recommendations from compact source-backed collaboration context.
- Sets up hosted EzDelegate: each user owns a Webex bot identity, the hosted shared listener classifies Webex requests, and task work opens in Desktop Codex.
- Shows a localhost-only local services dashboard for OAuth, Webex deeplink, My Day dashboard, attachment-action listener, and response-handler status.
- Searches and fetches Atlassian Cloud Confluence pages through the official Atlassian Rovo MCP server.
- Searches and fetches Cisco GPK2 Confluence Data Center pages from `confluence-eng-gpk2.cisco.com/conf` through a local read-only MCP server whenever the user asks for Confluence, unless they restrict the search to one site.
- Cross-references Webex and Confluence evidence with citations through the `answer-from-collaboration-history` skill.
- Stores each user's OAuth tokens locally under `~/.codex/webex-messaging/oauth-tokens.json`.

The plugin does not depend on Cisco's hosted Webex MCP servers for tool discovery. It exposes native REST-backed Webex tools and calls `https://webexapis.com/v1` directly with the signed-in user's token. For Atlassian Cloud Confluence, it uses Atlassian's official Rovo MCP server. For Cisco's internal GPK2 Confluence Data Center instance, it uses a local read-only REST-backed MCP server because Rovo MCP only covers Atlassian Cloud sites.

Message-returning tools include `sourceMarkdown`, `citation.markdown`, and `webexApp.messageUrl` fields. Skills instruct Codex to cite Webex message-derived answers by default using compact sender/time links, without requiring the user to ask for citations. Visible citations use a local `http://127.0.0.1` opener so Codex renders them as clickable links. When opened, the local proxy launches the raw `webexteams://im?space=<space_uuid>&message=<message_uuid>` app deeplink with the operating system instead of relying on the browser to handle the custom protocol. The raw app protocol URL is also returned as `webexApp.directMessageUrl`.

## Hosted EzDelegate

EzDelegate is hosted-first. Each user still creates and owns a Webex bot identity, but Cloud stores the bot token in the workspace secret/config model, registers that bot with the shared listener fleet, and keeps task/job state in the hosted Command Center store. The local lifecycle path is not part of the product surface for EzDelegate task execution.

For colleagues who need to create their own Webex Embedded App record that points at the shared hosted Command Center, see [Webex Embedded App Setup for EzWebex](docs/webex-embedded-app-setup.md).

The user-facing MCP tools remain available for setup and Desktop Codex handoff work:

- `webex-digital-twin-create-bot`: prepares suggested Developer Portal fields for a visible bot creation flow.
- `webex-digital-twin-setup`: writes hosted/shared-listener configuration and registers the bot.
- `webex-digital-twin-run`, `webex-digital-twin-status`, and `webex-digital-twin-stop`: report hosted/shared-listener state. Local lifecycle management is disabled.
- `webex-digital-twin-configure-triggers`: configures bot mentions by default. Owner mentions & DM, all-message listening, integration enrichment, and headless READ drafts require per-user feature flags.
- `webex-digital-twin-action-handoff`: loads a hosted handoff into the current Desktop Codex session, marks it actioned, and returns the Webex task context.
- `webex-digital-twin-submit-result`: sends the completed Desktop Codex outcome back to the hosted job for owner review and approval.

At runtime, the hosted listener processes live Webex events, dedupes by source message, builds bounded context packets, classifies candidate tasks, and creates or merges jobs into `awaiting_handoff` by default. Hosted Codex drafts are only available for flagged READ users. Normal task execution happens by opening the job in Desktop Codex through a `codex://new?prompt=...` deeplink or the hosted handoff URL; the prompt carries the job id and handoff URL, while the plugin loads full context from the hosted job API.

The default trigger policy is bot-mention-only: `botMention.enabled=true`, with `ownerMention`, `directMessages`, and `allSpaces` disabled unless the user's feature flags allow those modes.

## Local Services Status

EzWebex exposes a local observability surface for its helper processes through these MCP tools:

- `webex-get-local-services-status`: returns normalized status for local helpers.
- `webex-open-local-services-dashboard`: starts a localhost dashboard bound to `127.0.0.1`.
- `webex-stop-local-services-dashboard`: stops only the local services dashboard.

The v1 status surface tracks the OAuth callback helper, Webex deeplink opener, My Day dashboard, attachment-action listener, attachment response handlers, and the status dashboard itself. EzDelegate task execution is hosted and is intentionally not part of the local services dependency graph.

The MCP server also exposes prompt shortcuts named `ezdashboard` and `ezstatus` for clients that surface MCP prompts as slash commands. They are intended to map to `/ezdashboard` for the default My Day dashboard and `/ezstatus` for the Local Services dashboard.

## Human Network Routing

EzWebex is beginning to model collaboration as a private, user-scoped human network. The goal is not to rate people or build a company-wide org graph. The goal is to help the signed-in user find the right person to ask, choose the least disruptive path to an answer, and remember useful collaboration context over time.

The core idea is:

- Person profiles are the durable memory layer.
- Org chart data is only a hint.
- Project views are derived from people, not stored as a separate global project database.
- Routing recommendations are computed for a specific goal and are not persisted as global scores.
- Webex cards can be used as lightweight referral probes: ask a first person, capture whether they can help or who to ask next, then save only compact source-backed learnings.

Examples this is meant to support:

- "Who should I ask about SCIM org-chart data?"
- "Who is a warm first person to ask about this project?"
- "What is the smallest useful Webex space to ask?"
- "Can you ask Alice who owns this and wait for her response?"
- "Follow the referral, but ask me before messaging the next person."

The v1 tool for this is `webex-find-who-to-ask`. It uses cached person profiles as private planning hints, can optionally refresh explicit target or candidate profiles, and returns goal-specific recommendations with reasoning, confidence, risks, mitigations, source references, and an optional draft ask. If a referral probe should be sent, EzWebex reuses the existing `webex-poll-for-response` and attachment-action tools rather than creating a separate card system.

```text
User goal -> find who to ask -> first person -> Webex referral card -> response -> profile memory update -> optional next person
```

Privacy and safety are part of the design:

- Profiles are scoped to the signed-in user.
- EzWebex avoids raw message or transcript storage in durable memory.
- Durable observations need source references unless they are explicitly confirmed private notes.
- Private notes and private impressions require confirmation.
- The system does not store global people scores, character labels, motive inference, or rankings.
- Any outbound Webex message or card informed by profile memory is shown to the user for approval first.

There is a light Cisco-flavored routing metaphor under the hood: a first person to ask, warm path, max hops, loop detection, smallest useful audience, and right-sized ask. The user-facing behavior should still feel like normal collaboration: "who should I ask, and how do I ask without spamming everyone?"

## How Profiles Improve Over Time

EzWebex person profiles are not meant to be manually filled out all at once. They become useful gradually as the plugin helps with meeting prep, message search, follow-ups, and referral probes.

When a person becomes relevant to a task, EzWebex can refresh their directory and collaboration hints. When there is a durable planning lesson, EzWebex can save a compact profile update: a project association, an open ask, a collaboration preference, a useful operating pattern, or a planning risk with mitigation.

Read-heavy tools also do a small amount of opportunistic profile seeding. Message, thread, membership, meeting, and My Day snapshot calls can update deterministic hints such as identity, shared spaces, shared meetings, and recent interaction signals from data they already returned. This path is bounded, local, and does not perform extra People or SCIM refreshes; set `autoUpdatePersonProfiles: false`, `profileSeedMaxPeople: 0`, or `WEBEX_MCP_AUTO_PROFILE_SEED=false` to skip it.

This makes future "who should I ask?" and "what is the best path to an answer?" questions more accurate, because the routing layer has better private context to work from.

EzWebex should avoid saving noise. It should not remember every message. It should remember compact, source-backed context that helps the signed-in user collaborate more effectively.

Profile update rule of thumb:

- Refresh profiles when a person becomes important to the current task.
- Save profile memory only when there is a durable planning benefit.
- Do not save every interaction.
- Do not save raw messages, transcripts, or long excerpts.
- Do not save speculative judgments.
- Prefer source-backed operational facts over impressions.

Useful updates usually belong in these profile sections:

- `relationshipToMe`: shared spaces, shared meetings, recent interaction signals, and relationship summaries.
- `activeThreads`: open asks, commitments, and pending decisions.
- `projectHints`: project or topic involvement, role, status, aliases, and source references.
- `collaborationPreferences`: practical engagement preferences such as concise async context, options with tradeoffs, or project-space discussion over DMs.
- `operatingPatterns.agencyHints`: contextual, positive planning patterns such as unblocking paths or cross-functional bridging, with planning use, confidence, timestamp, and source references.
- `operatingPatterns.planningRisks`: behavior-based risks with impact and mitigation, such as ownership ambiguity causing next steps to stall.
- `memoryFacts`: compact durable facts like role, area, ownership, expertise, team, project affiliation, or working context.
- `privateNotes`: user-confirmed freeform notes only.

## Install

Add the marketplace:

```bash
codex plugin marketplace add padmanak_cisco/webex-messaging-codex-plugin --ref main
codex plugin marketplace upgrade
```

Then in Codex:

1. Open `/plugins`.
2. Choose `Webex Codex Plugins`.
3. Install `EzWebex`.
4. Start a fresh thread and tag `@ezwebex`.

Codex should open a browser-based Webex sign-in flow the first time the plugin needs a Webex token. Atlassian should open its own browser-based OAuth 2.1 flow only after an Atlassian/Rovo tool is called, not when the plugin is merely loaded or its tool list is inspected.

If the private GitHub marketplace is unavailable from a machine, use the hosted ZIP bundle:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co/ezwebex-codex-plugin.zip
```

The hosted setup page is:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co/
```

## Update Existing Install

If you already installed EzWebex from the marketplace, run:

```bash
codex plugin marketplace upgrade webex-codex-plugins
```

Then open a fresh Codex thread and tag `@ezwebex` so Codex reloads the updated plugin tools and skills.

If the marketplace path does not work because of GitHub access, download the latest ZIP instead:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co/ezwebex-codex-plugin.zip
```

Then open `/plugins` in Codex and import or reinstall the local plugin bundle.

The hosted ZIP is generated from `main` and cached briefly, so it can take about a minute after a merge before the download reflects the newest changes.

## Automated Release Announcements

The preferred release flow is Git-based:

1. Make the plugin change.
2. Push to `main`.
3. The CINT broker receives the GitHub push webhook, verifies the signature, refreshes the hosted ZIP bundle, and posts an update card with the updater identity to the private `EzPlugin` Webex space.

This does not require GitHub Actions. It requires a GitHub repository webhook pointed at:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co/github-webhook
```

The CINT broker secret must include:

```text
GITHUB_WEBHOOK_SECRET
WEBEX_UPDATE_ACCESS_TOKEN
```

`WEBEX_UPDATE_ACCESS_TOKEN` should be a Webex bot token or another server-side token for an account that is a member of `EzPlugin`.

For manual releases, this repo also includes a maintainer-only Codex skill:

```text
.codex/skills/webex-plugin-release/SKILL.md
```

Keep this skill outside `plugins/webex-messaging/skills/` so it does not ship to every plugin installer or expose internal release-channel details in the customer-facing plugin bundle.

If the webhook is unavailable, EzDubs OG contributors can use the manual release flow:

1. Clone this repo, or run `git pull` in an existing checkout so `.codex/skills/webex-plugin-release/SKILL.md` is present locally.
2. From the repo root, make the plugin change with Codex.
3. Ask Codex:

```text
Use the repo skill at .codex/skills/webex-plugin-release/SKILL.md to ship this Webex plugin update.
```

4. Codex should validate the change, bump the plugin version when needed, commit and push to `main`, wait for the hosted ZIP metadata to reflect the new commit, then post the update card to `EzPlugin`.

## Authentication

This plugin has two authentication paths.

### Webex

The OAuth flow is split deliberately:

1. Codex starts the plugin's local MCP proxy.
2. The proxy briefly listens on a localhost callback URL.
3. The hosted CINT broker redirects the browser to Webex OAuth and exchanges auth codes for tokens.
4. The broker keeps the Webex client secret server-side.
5. The plugin stores only the signed-in user's OAuth tokens locally.

Allowed callback URIs for CINT and local plugin flows:

```text
https://ezdubs-codex.uscentral1-0.cint.vcra.co/oauth/webex/callback
http://localhost:51236/callback
http://localhost:53648/callback
http://localhost:57233/callback
```

The hosted broker is:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co
```

Set the Webex Developer Portal integration name to `EzWebex Plugin`. Webex uses that external integration name in the message byline when EzWebex sends messages with the signed-in user's OAuth token.

The Webex integration must be configured with scopes that exactly match what the broker requests. The current CINT deployment requests:

```text
spark:all
spark:mcp
spark:webhooks_read
spark:webhooks_write
identity:people_read
meeting:schedules_read
meeting:schedules_write
meeting:participants_read
meeting:admin_participants_read
meeting:summaries_read
meeting:recordings_read
meeting:transcripts_read
```

`identity:people_read` enables best-effort SCIM 2.0 reads for person-profile manager and organization hints. If a stored token is missing a scope required by a tool, the proxy opens Webex sign-in once, preserves the old token until the upgraded grant is valid, and retries. If Webex still does not grant the scope, the proxy records the failed upgrade and suppresses repeat browser prompts for that scope until logout/reset or the retry window expires.

For meeting recap requests, the plugin first checks `/meetingSummaries` and `/meetingTranscripts`. If those meeting-level APIs are empty or unavailable, it falls back to recording recaps. Recording recaps use the signed-in user's normal Webex OAuth token to read the same Webex User Hub recording-player data shown in shared recording pages and emails. This lets the plugin retrieve AI-generated recording notes, chapters, and action items when `/meetingSummaries` or `/meetingTranscripts` do not expose those artifacts.

`meeting:admin_participants_read` lets the OAuth grant include the admin participant permission that Webex requires for participant lookup on meetings the signed-in user does not host. These scopes do not grant the user Webex admin powers by themselves: Webex can still return 401/403 when the signed-in user lacks the required host, site admin, org admin, role, or license entitlement.

Do not use `codex mcp login` for this plugin. Webex's MCP OAuth metadata does not support dynamic client registration for this use case; the plugin owns OAuth through the local proxy and hosted broker.

### Amplitude Analytics

EzWebex can send silent, best-effort Amplitude events from the auth broker and local MCP proxy. These are ordinary background `fetch` calls made by the hosted broker, `server/webex-oauth-proxy.mjs`, and the Atlassian/Rovo wrapper; they are not exposed as MCP tools, tool results, or user-facing Codex workflow steps.

Configure the production Amplitude project through both MCP server environment blocks:

```json
{
  "EZWEBEX_AMPLITUDE_API_KEY": "public-project-key",
  "EZWEBEX_AMPLITUDE_SERVER_ZONE": "EU"
}
```

Analytics stays off unless `EZWEBEX_AMPLITUDE_API_KEY` is present. Use `EZWEBEX_AMPLITUDE_DISABLED=true` or `EZWEBEX_ANALYTICS_DISABLED=true` to force-disable it. The HTTP endpoint defaults to Amplitude EU ingestion and can be overridden with `EZWEBEX_AMPLITUDE_ENDPOINT` for approved testing.

For the hosted ZIP flow, set `EZWEBEX_AMPLITUDE_API_KEY` and `EZWEBEX_AMPLITUDE_SERVER_ZONE` on the auth broker. The GitHub-backed bundle builder injects those values into the generated `.mcp.json` so the checked-in plugin config does not need to contain the project key.

The broker and proxy identify Amplitude users with the Cisco email from the Webex OAuth identity (`id_token` when available, otherwise `/people/me`). Non-user entity properties are pseudonymized before leaving the process. Events intentionally avoid message text, room names, meeting titles, file names, access tokens, raw Webex IDs, and raw query parameters.

Tracked events:

- `ezwebex_oauth_completed`: hosted broker completed Webex OAuth for a Cisco email.
- `ezwebex_user_signed_up`: first local sighting of a signed-in email.
- `ezwebex_user_active_day`: once per signed-in user per UTC day for retention analysis.
- `ezwebex_user_authenticated`: signed-in user identity available for this proxy session.
- `ezwebex_mcp_session_started`: MCP proxy session started for a signed-in user.
- `ezwebex_mcp_tools_listed`: MCP tool surface was listed.
- `ezwebex_mcp_tool_called`: tool distribution, success, latency, workflow, skill hint, and MCP name.
- `ezwebex_workflow_invoked`: workflow-level rollup for common usage analysis.
- `ezwebex_webex_rest_request`: sanitized Webex REST method, route template, status, latency, and associated MCP tool/workflow.

Recommended Amplitude rollout dashboard:

- MCP tools per day: event `ezwebex_mcp_tool_called`, metric `Event Totals`, interval `Daily`, group by `tool_name`, and optionally filter or split by `mcp_name`.
- MCPs per day: event `ezwebex_mcp_tool_called`, group by `mcp_name`, and optionally split by `tool_name`, `skill_hint`, or `workflow`.
- Skills per day: event `ezwebex_mcp_tool_called`, group by `skill_hint`, and optionally split by `mcp_name`, `tool_name`, or `workflow`.
- Signups and activation: funnel `ezwebex_oauth_completed` -> `ezwebex_mcp_session_started` -> `ezwebex_mcp_tool_called`.
- Retention: retention chart with starting event `ezwebex_user_signed_up` or `ezwebex_oauth_completed` and returning event `ezwebex_user_active_day`.
- Common workflows: event `ezwebex_workflow_invoked`, group by `workflow`, `skill_hint`, and `tool_family`.
- Webex API health: event `ezwebex_webex_rest_request`, group by `route`, `status`, `success`, and `tool_name`.

The machine-checkable dashboard recipe lives in `scripts/amplitude-dashboard-spec.json`. Use the `analytics_seed=true` filter only while validating chart shape with synthetic events; production views should exclude `analytics_seed=true`.

After the EzWebex Amplitude project key exists, use the opt-in seed script to validate the MCP-tools-per-day chart with synthetic data marked `analytics_seed: true`:

```bash
EZWEBEX_AMPLITUDE_API_KEY=... EZWEBEX_AMPLITUDE_SERVER_ZONE=EU \
  node scripts/seed-amplitude-mcp-tools.mjs --send --days 3 --events-per-day 12
```

### Atlassian Cloud Confluence

Confluence access is handled by a lazy local wrapper around the official Atlassian Rovo MCP server configured in `plugins/webex-messaging/.mcp.json`:

```text
https://mcp.atlassian.com/v1/mcp/authv2
```

The local MCP process is:

```text
node ./server/atlassian-rovo-mcp-remote.mjs
```

At plugin load, that wrapper returns a static Atlassian tool list without launching Atlassian OAuth. If `ATLASSIAN_MCP_AUTH_HEADER` or `ATLASSIAN_MCP_AUTH_HEADER_FILE` is configured, the wrapper passes that Authorization header to the remote MCP server. Otherwise, on the first actual Atlassian/Rovo tool call, it launches the local OAuth flow:

```text
npx -y mcp-remote@latest https://mcp.atlassian.com/v1/mcp/authv2 --host 127.0.0.1 --auth-timeout 180
```

Before that demand-loaded launch, it checks `~/.mcp-auth/mcp-remote-*` for stale Atlassian OAuth coordination files. If a previous login attempt left a dead `lock.json` or tokenless partial state such as `code_verifier.txt` and `client_info.json`, the wrapper removes only that partial state and starts a clean flow. It preserves valid `tokens.json` files.

Atlassian Rovo MCP uses Atlassian OAuth 2.1, respects the signed-in user's existing Atlassian Cloud permissions, and is governed by Atlassian organization settings such as user-installed app policy, tool permission groups, domain controls, IP allowlisting, and audit logging. It exposes Confluence tools such as `searchConfluenceUsingCql`, `getConfluencePage`, `getConfluenceSpaces`, and `getPagesInConfluenceSpace`, plus Rovo search tools such as `searchAtlassian` and `fetchAtlassian`.

If Atlassian login opens but the browser later shows `localhost refused to connect`, the usual cause is an interrupted or stale OAuth attempt where the `mcp-remote` callback process has already exited. Start a fresh Codex thread or retag `@ezwebex`; the wrapper should clear the stale partial cache and reopen a live callback listener.

If Cisco's Atlassian administration disables the Rovo MCP server, blocks external MCP clients/domains, blocks the current IP range, or keeps content on a non-Cloud Confluence deployment, the official remote MCP path may not work. The included GPK2 MCP server covers Cisco's `confluence-eng-gpk2.cisco.com/conf` deployment; other non-Cloud instances should use the same narrow read-only pattern with Cisco-approved authentication.

### Cisco GPK2 Confluence

Cisco GPK2 Confluence access is handled by a local read-only MCP server configured in `plugins/webex-messaging/.mcp.json`:

```text
https://confluence-eng-gpk2.cisco.com/conf
```

The local MCP process is:

```text
node ./server/confluence-gpk2-mcp.mjs
```

This server exposes:

```text
confluence-gpk2-auth-status
confluence-gpk2-setup-auth
confluence-gpk2-clear-auth
confluence-gpk2-search
confluence-gpk2-get-page
confluence-gpk2-get-page-children
confluence-gpk2-get-page-labels
confluence-gpk2-get-page-comments
confluence-gpk2-get-space
```

Use these tools for `confluence-eng-gpk2.cisco.com/conf/...` links and page IDs. The official Atlassian Rovo MCP server is a Cloud gateway, so it should not be expected to find GPK2 Confluence Data Center pages unless Cisco has mirrored that content into Atlassian Cloud.

The `answer-from-collaboration-history` skill treats a generic "search/use Confluence" request as both Atlassian Cloud/Rovo and GPK2 search. It only opens the GPK2 PAT setup helper after a relevant GPK2 search/fetch is actually blocked by authentication, or when the user explicitly asks to set up GPK2 auth.

Anonymous REST reads work for some public GPK2 pages, but permission-restricted pages require local credentials. The preferred option is a Confluence Data Center personal access token stored outside chat. Cisco's GPK2 token UI is:

```text
https://confluence-eng-gpk2.cisco.com/conf/plugins/personalaccesstokens/usertokens.action
```

The easiest setup is the packaged local helper. In Codex, ask EzWebex to set up GPK2 Confluence auth, or call:

```text
confluence-gpk2-setup-auth
```

That tool opens a local browser page on `127.0.0.1`, links to the GPK2 token UI, accepts the token in the browser, validates it with `/rest/api/user/current`, and stores it under `~/.codex/confluence-gpk2/pat` with `0600` permissions. The token is not pasted into chat and is not sent to any external setup service.

Manual setup is still supported:

```bash
mkdir -p ~/.codex/confluence-gpk2
chmod 700 ~/.codex/confluence-gpk2
printf '%s' '<your-gpk2-confluence-pat>' > ~/.codex/confluence-gpk2/pat
chmod 600 ~/.codex/confluence-gpk2/pat
```

The server accepts these GPK2-specific environment variables:

- Base URL: `CONFLUENCE_GPK2_BASE_URL`
- Token: `CONFLUENCE_GPK2_PAT` or `CONFLUENCE_GPK2_PAT_FILE`
- Basic auth: `CONFLUENCE_GPK2_USERNAME` plus the PAT variable
- Explicit auth/header cases: `CONFLUENCE_GPK2_AUTH_HEADER`, `CONFLUENCE_GPK2_AUTH_HEADER_FILE`, or `CONFLUENCE_GPK2_CUSTOM_HEADERS`
- TLS override for internal certificate issues: `CONFLUENCE_GPK2_SSL_VERIFY=false`
- Local setup helper ports: `CONFLUENCE_GPK2_SETUP_PORTS`, default `51238,53650,57235`

Do not paste GPK2 credentials into chat.

Docker is not required for EzWebex GPK2 access. The `rahusoni/atlas-mcp-server` project ships a standalone Python/Docker MCP server for broader non-cloud Jira/Confluence use. EzWebex packages the narrower read-only GPK2 path directly as Node code inside the plugin.

## Confluence Implementation Decision

The implementation uses the official Atlassian Rovo MCP server for Atlassian Cloud and a narrow local read-only MCP server for Cisco GPK2 Confluence Data Center. It borrows the useful configuration conventions from Cisco's internal `rahusoni/atlas-mcp-server` project while avoiding that server's heavier Docker/Python runtime and broad write-capable Jira/Confluence tool surface inside EzWebex.

Reasoning:

- Cisco enterprise/admin policy: Prefer official MCP first because it has organization controls, OAuth 2.1, permission enforcement, and audit logging. Build a custom proxy only if Cisco blocks Rovo MCP, blocks the required client/domain/IP, or requires Cisco-owned service mediation.
- Cloud vs Data Center: Rovo MCP is for Atlassian Cloud. The plugin includes a local read-only GPK2 Confluence Data Center MCP server for Cisco internal pages under `confluence-eng-gpk2.cisco.com/conf`.
- Tool coverage: Rovo MCP already exposes CQL search, page fetch, space/page navigation, comments, Rovo search, and Teamwork Graph relationship tools. Build custom only if we need result shaping, citation payloads, indexing, or endpoints that Rovo MCP does not expose.
- Governance/control: Rovo MCP is better for user-scoped access and auditability on Cloud. The local GPK2 server is intentionally read-only and avoids write tools, attachment upload, or service-account defaults.

## Developer Notes

Important paths:

```text
plugins/webex-messaging/                 Codex plugin bundle
plugins/webex-messaging/.mcp.json        Local MCP proxy config
plugins/webex-messaging/server/          Native REST-backed MCP server
plugins/webex-messaging/skills/          Webex and Confluence workflow skills
.codex/skills/webex-plugin-release/      Maintainer-only release workflow
auth-broker/                             Hosted OAuth broker
auth-broker/Dockerfile                   Immutable auth broker image
../k8s-config/ezdubs/ezwebex-auth-broker CINT auth broker Kubernetes release
../k8s-config/ezdubs/ezwebex-control-plane CINT control plane Kubernetes release
../k8s-config/ezdubs/ezwebex-listener-fleet Shared listener Kubernetes release
../k8s-config/ezdubs/ezwebex             Aggregate EzWebex release orchestration
index.html                               Public setup page served by the broker
```

Useful checks:

```bash
python3 -m json.tool plugins/webex-messaging/.codex-plugin/plugin.json >/dev/null
python3 -m json.tool plugins/webex-messaging/.mcp.json >/dev/null
python3 -m json.tool .agents/plugins/marketplace.json >/dev/null
node --check plugins/webex-messaging/server/atlassian-rovo-mcp-remote.mjs
node --check plugins/webex-messaging/server/amplitude-analytics.mjs
node --check plugins/webex-messaging/server/confluence-gpk2-mcp.mjs
node --check plugins/webex-messaging/server/webex-oauth-proxy.mjs
node --check auth-broker/server.mjs
node scripts/check-gpk2-mcp-tools.mjs
node scripts/check-mcp-tools.mjs
node scripts/check-analytics.mjs
node scripts/seed-amplitude-mcp-tools.mjs
```

Refresh the local plugin cache during development:

```bash
PLUGIN_VERSION=$(node -e "console.log(JSON.parse(require('fs').readFileSync('plugins/webex-messaging/.codex-plugin/plugin.json', 'utf8')).version)")
rm -rf ~/.codex/plugins/cache/webex-codex-plugins/ezwebex
mkdir -p ~/.codex/plugins/cache/webex-codex-plugins/ezwebex/$PLUGIN_VERSION
rsync -a --delete plugins/webex-messaging/ ~/.codex/plugins/cache/webex-codex-plugins/ezwebex/$PLUGIN_VERSION/
```

Avoid duplicate local marketplaces named `webex-codex-plugins`; Codex may otherwise rebuild the cache from the wrong plugin source.

Build and push EzWebex images from this repo:

```bash
make everything-build-push
```

Deploy all EzWebex Kubernetes services with one source SHA, one image tag set, and matching pod annotations:

```bash
make -C ../k8s-config/ezdubs/ezwebex release-deploy CONTEXT=cint-uscentral1-0-0
```

The broker serves generated bundle metadata at:

```text
https://webex-codex-auth.uscentral1-0.cint.vcra.co/ezwebex-codex-plugin.json
```

The CINT broker pulls `main` from GitHub server-side, builds the plugin ZIP in memory from an allowlist, and caches the generated bundle for 60 seconds. Its Vault-backed `ezwebex-auth-broker` Kubernetes Secret must include `WEBEX_CLIENT_ID`, `WEBEX_CLIENT_SECRET`, and a read-only `GITHUB_TOKEN` for this private repository.

If automated release announcements are enabled, the same secret must also include `GITHUB_WEBHOOK_SECRET` and `WEBEX_UPDATE_ACCESS_TOKEN`.
