NyxID ships to agent stores as thin plugin packages around one hosted MCP server, https://nyx-api.chrono-ai.fun/mcp. Every behavior lives on that server: tools, OAuth sign-in, consent, tool annotations, connection flows. A package holds only a manifest, a pointer to the server, one MCP-first skill, and icons. It runs no local code.
Server changes therefore reach every installed plugin when the backend deploys. A package only changes when its listing metadata or skill changes.
| Path | Store | Generated? |
|---|---|---|
integrations/plugin-source/ |
Single source for Claude and Codex | Source |
integrations/claude-plugin/ |
Claude directory, Claude Code | Manifest, .mcp.json, skill |
integrations/codex-plugin/ |
OpenAI plugin directory (ChatGPT, Codex) | Manifest, mcp.json, skill |
.claude-plugin/marketplace.json |
claude plugin marketplace add ChronoAIProject/NyxID |
Yes |
.agents/plugins/marketplace.json |
codex plugin marketplace add ChronoAIProject/NyxID |
Yes |
.claude-plugin/plugin.json + skills/ |
nyxid-cli: CLI edition for Claude Code, installed by nyxid ai-setup |
No |
integrations/cursor-plugin/ |
Cursor | No (docs/CURSOR_PLUGIN.md) |
Each package's README, LICENSE and assets/ stay hand-written.
The Claude marketplace lists two plugins:
nyxidis the package submitted to the Claude directory. Its MCP server is registered asplugin:nyxid:nyxid.nyxid-cliis the CLI skill bundle at the repository root, including the Aevatar family and the service skills.
The Codex marketplace lists only nyxid.
- Edit
integrations/plugin-source/plugin.jsonfor the version, MCP URL, listing links, descriptions and default prompts, orintegrations/plugin-source/skills/nyxid/SKILL.mdfor the skill.- The skill template takes
{{clients}},{{assistant}},{{mcp_url}},{{homepage}}and{{manage_access_url}}. - Mention MCP tools by their exact
nyx__*names.
- The skill template takes
- Run
python3 scripts/sync-plugins.pyand commit the regenerated files with the source. - Bump
versioninplugin-source/plugin.jsonwhenever a package's files change. Both packages share one version.- Codex also shows
stores.codex.release_notesto reviewers. - The CLI edition keeps its own version in
.claude-plugin/plugin.json.
- Codex also shows
Never edit generated files by hand. CI fails when they differ from the source.
- Generated files:
scripts/sync-plugins.py --check(CI: Codex and Claude Plugin Validate) fails when a generated file drifts from the source. - Packages:
scripts/build-codex-plugin.sh --checkandscripts/validate-claude-plugin.py --require-claudevalidate the packages and the store bundle limits.claude plugin validate --strict .validates the Claude marketplace. - Skill vs server contract: the backend test
plugin_skill_only_names_real_annotated_meta_tools(backend/src/services/mcp_service.rs) fails when the skill names anyx__*tool that the server does not define or that has notitle,readOnlyHint,destructiveHintandopenWorldHintannotations. The Claude directory requires a title and a read-only or destructive hint on every tool;tool_titleandtool_annotationsin the same file supply them, andtools/listpublishes the title both at the top level and inannotations. Rename a meta tool and the skill in the same change.
| Change | Action |
|---|---|
| Backend MCP or OAuth behavior | Deploy the backend. No store action; the stores' scans read the live server. |
| Skill or listing metadata | Bump the version, run sync-plugins.py, merge, then publish both stores below. |
- Codex / ChatGPT: run
scripts/build-codex-plugin.shand uploaddist/nyxid-codex-plugin-<version>.zipas a new version in the OpenAI plugin portal (docs/CODEX_PLUGIN.md). - Claude: resubmit from https://claude.ai/directory/manage (docs/CLAUDE_PLUGIN.md). Claude Code users get the new version through
claude plugin update nyxid@nyxid.
Plugins sign in with OAuth dynamic client registration. On the consent page the user chooses which services the app may use. A restricted or zero-service grant is kept through code exchange and refresh. The skill's "When services are missing" section tells the agent to send the user to /settings/consents to revoke and reconnect the app. nyx__connect_service returns service_not_granted with the same link when a restricted app asks to connect a new service.
MCP sessions created by tokens issued to an OAuth client, or by tokens restricted to certain services or nodes, never authenticate on their own: every request must carry a live bearer token, so the grant always applies. This holds once every backend replica runs a build with session_fallback_allowed; during a rolling deploy older replicas still honor the fallback. Sessions created before that build (no flag) need a live bearer once; clients refresh on the 401.
For MFA-enabled accounts, fallback also requires the exact live parent human session to carry MFA assurance. Legacy MCP rows without a parent and sessions whose parent has expired, was revoked, or lacks assurance receive 401. Sign in again with MFA and initialize a new connection. An unrelated assured login does not authorize an older MCP session.
Connections recover without user action. A request naming an unknown, expired or foreign Mcp-Session-Id gets HTTP 404 (JSON-RPC body -32002), which Streamable HTTP clients answer by initializing a new session with the credential they hold. Ending a NyxID login, and server restarts, only remove session-fallback sessions, so OAuth clients' sessions survive both. On /oauth/token, every dead refresh token (malformed, expired, revoked, rotated past the grace period, or unknown) returns 400 invalid_grant, which makes Claude and Codex clear their tokens and ask the user to sign in again; a deactivated issuing client returns 401 invalid_client so the client re-registers.
The install commands (claude mcp login) and claude plugin validate --strict need Claude Code 2.1.284 or later (CI pins CLAUDE_CODE_VERSION). On older versions, run /mcp inside Claude Code to sign in.
A user can hold up to 50 MCP sessions. When those slots are full, initialization
atomically replaces the oldest session that has received no client activity for
at least one hour. Recent sessions are protected; if every session is recent,
initialization still reports Too many active MCP sessions. Replacement keeps
the slot count bounded across replicas, removes the old notifications, and
creates fresh authority and activation state. A client returning to a replaced
session receives HTTP 404 and initializes again with its live credential.
Sessions below capacity retain the existing 30-day idle lifetime.
Initialization echoes the supported client version (2025-06-18 or
2025-11-25); an unsupported version receives the server's latest supported
version, as required by MCP version negotiation.
See the MCP session lifecycle
and version negotiation
contracts for termination, HTTP 404 recovery and supported-version selection.
The consent page offers Off and Allow. Allow authorizes the service's
available operations, including reads and writes, subject to the credential's
permissions and configured approval policies. OAuth service grants do not
currently enforce separate read-only access. MCP readOnlyHint annotations
describe tools to clients; they are not authorization checks.
Services start off unless preselected by the app or required by a resource
request. App preselection can be removed. All services explicitly includes
future connections; turning any service off submits an explicit allowlist,
which does not include future connections. The server rejects unsupported
write_access=none or selected submissions from older cached consent pages
instead of silently granting more access than the page describes. Existing
grants have no stored read/write distinction; users who intended read-only
access must revoke those grants and review access again.