Skip to content

Latest commit

 

History

History
106 lines (78 loc) · 8.33 KB

PLUGINS.md

File metadata and controls

106 lines (78 loc) · 8.33 KB

Agent plugins: maintenance guide

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.

Layout

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:

  • nyxid is the package submitted to the Claude directory. Its MCP server is registered as plugin:nyxid:nyxid.
  • nyxid-cli is the CLI skill bundle at the repository root, including the Aevatar family and the service skills.

The Codex marketplace lists only nyxid.

Changing a plugin

  1. Edit integrations/plugin-source/plugin.json for the version, MCP URL, listing links, descriptions and default prompts, or integrations/plugin-source/skills/nyxid/SKILL.md for the skill.
    • The skill template takes {{clients}}, {{assistant}}, {{mcp_url}}, {{homepage}} and {{manage_access_url}}.
    • Mention MCP tools by their exact nyx__* names.
  2. Run python3 scripts/sync-plugins.py and commit the regenerated files with the source.
  3. Bump version in plugin-source/plugin.json whenever a package's files change. Both packages share one version.
    • Codex also shows stores.codex.release_notes to reviewers.
    • The CLI edition keeps its own version in .claude-plugin/plugin.json.

Never edit generated files by hand. CI fails when they differ from the source.

Guardrails

  • 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 --check and scripts/validate-claude-plugin.py --require-claude validate 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 a nyx__* tool that the server does not define or that has no title, readOnlyHint, destructiveHint and openWorldHint annotations. The Claude directory requires a title and a read-only or destructive hint on every tool; tool_title and tool_annotations in the same file supply them, and tools/list publishes the title both at the top level and in annotations. Rename a meta tool and the skill in the same change.

Releasing

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.

App access and consent

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.

Requirements

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.

Connection limits and protocol versions

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.

Service permissions on consent

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.