Skip to content

Commit 41feba4

Browse files
Aditya Thiruvengadamclaude
andcommitted
feat: add Codex plugin + marketplace generated from metadata
Mirror the Claude Code plugin pattern for OpenAI's Codex CLI plugin ecosystem: - klarity_mcp/metadata.py: add CODEX_PLUGIN_DIR (.codex-plugin/) and CODEX_MARKETPLACE_PATH (.agents/plugins/marketplace.json) — the canonical lookup locations per openai/codex codex-rs/core-plugins/src/marketplace.rs. Also switch brand_color from the #2F6BFF placeholder to the actual Klarity orange #FF6C14. - klarity_mcp/builders.py: add build_codex_plugin_manifest and build_codex_marketplace_manifest. The plugin manifest reuses the shared .mcp.json at the repo root (mcpServers: "./.mcp.json") and the bundled SKILL.md under skills/, and adds the interface block Codex uses to render install-card metadata. The marketplace plugin source uses {source: "url", url: "<repo>.git"} so the entire repo acts as the plugin without duplicating manifests under .agents/plugins/. - klarity_mcp/__init__.py: export the new paths. - tests/test_packaging.py: add four Codex invariants — required fields and capped defaultPrompt, mcpServers/skills paths resolve to real artifacts, marketplace plugin source matches the supported shape (local/url/git-subdir + policy enum). - .codex-plugin/plugin.json, .agents/plugins/marketplace.json: generated files committed so `/plugin marketplace add` resolves this repo as a Codex marketplace. Also update install instructions: - README.md, docs/install/codex.mdx: document the `/plugin marketplace add` + `/plugin install` flow as the preferred Codex path. Strip internal manifest-location explanations from both README install sections (Claude Code + Codex) — end users don't need to know where the manifests live. Unrelated doc tweak in the same push: revise the Improve stage on docs/concepts/discover-structure-improve.mdx to name both Advisor and the MCP as analysis surfaces (build skills and agents grounded in how the business runs), and drop the Signals/objective-tools trailing sentences. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6147cca commit 41feba4

9 files changed

Lines changed: 253 additions & 28 deletions

File tree

.agents/plugins/marketplace.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "klarity",
3+
"interface": {
4+
"displayName": "Klarity"
5+
},
6+
"plugins": [
7+
{
8+
"name": "klarity",
9+
"source": {
10+
"source": "url",
11+
"url": "https://github.com/Klarity-AI/klarity-mcp.git"
12+
},
13+
"policy": {
14+
"installation": "AVAILABLE",
15+
"authentication": "ON_INSTALL"
16+
},
17+
"category": "Business"
18+
}
19+
]
20+
}

.codex-plugin/plugin.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "klarity",
3+
"version": "1.0.0",
4+
"description": "Bring your organization's processes and operational knowledge into your AI assistant. The Klarity MCP connects to your Klarity workspace so you can query your organization's processes, explore the process index knowledge graph linking processes to systems and teams, and ground answers in how your business actually runs \u2014 not generic guesses.",
5+
"author": {
6+
"name": "Klarity Intelligence, Inc.",
7+
"email": "hello@klarity.ai",
8+
"url": "https://www.klarity.ai/"
9+
},
10+
"homepage": "https://www.klarity.ai/",
11+
"repository": "https://github.com/Klarity-AI/klarity-mcp",
12+
"license": "Apache-2.0",
13+
"keywords": [
14+
"klarity",
15+
"mcp",
16+
"process-intelligence",
17+
"process-index",
18+
"context-graph",
19+
"knowledge-graph"
20+
],
21+
"skills": "./skills/",
22+
"mcpServers": "./.mcp.json",
23+
"interface": {
24+
"displayName": "Klarity",
25+
"shortDescription": "Explore your org's processes",
26+
"longDescription": "Bring your organization's processes and operational knowledge into your AI assistant. The Klarity MCP connects to your Klarity workspace so you can query your organization's processes, explore the process index knowledge graph linking processes to systems and teams, and ground answers in how your business actually runs \u2014 not generic guesses.",
27+
"developerName": "Klarity Intelligence, Inc.",
28+
"category": "Business",
29+
"capabilities": [
30+
"Read",
31+
"Analyze"
32+
],
33+
"websiteURL": "https://www.klarity.ai/",
34+
"privacyPolicyURL": "https://www.klarity.ai/product-privacy-policy",
35+
"termsOfServiceURL": "https://www.klarity.ai/terms-of-service-2025",
36+
"defaultPrompt": [
37+
"Find me the invoice intake process in my Klarity Workspace.",
38+
"How does my invoice intake process work?",
39+
"Show me the procure-to-pay process and its dependencies in my Klarity Workspace."
40+
],
41+
"brandColor": "#FF6C14"
42+
}
43+
}

README.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,20 @@ Apps -> Search -> Klarity -> Connect
3737
3838
## Install — Claude Code
3939

40-
From a Claude Code session, add this repository as a marketplace, then install:
40+
From a Claude Code session:
4141

4242
```text
4343
/plugin marketplace add klarity-ai/klarity-mcp
4444
/plugin install klarity@klarity
4545
```
4646

47-
The `marketplace add` step points Claude Code at this repo's
48-
[`.claude-plugin/marketplace.json`](./.claude-plugin/marketplace.json). The
49-
install step then resolves `klarity@klarity` as plugin `klarity` from
50-
marketplace `klarity`.
47+
## Install — Codex
5148

52-
## Install - Codex
53-
From your terminal, run the following:
49+
From a Codex session:
5450

5551
```text
56-
codex mcp add klarity --url https://architect-v2-api.klarity.ai/mcp
57-
```
58-
59-
To login:
60-
```text
61-
codex mcp login klarity
52+
/plugin marketplace add klarity-ai/klarity-mcp
53+
/plugin install klarity@klarity
6254
```
6355

6456
## Install — Gemini CLI
@@ -90,7 +82,9 @@ to Klarity. the MCP will use whatever authentication is configured by your organ
9082
| `klarity_mcp/` | Python package: metadata + builders + CLI for regenerating manifests |
9183
| `.claude-plugin/plugin.json` | Claude Code plugin manifest (generated) |
9284
| `.claude-plugin/marketplace.json` | Claude Code marketplace catalog (generated) |
93-
| `.mcp.json` | Claude Code MCP server config (generated, HTTP transport) |
85+
| `.codex-plugin/plugin.json` | Codex plugin manifest (generated) |
86+
| `.agents/plugins/marketplace.json` | Codex marketplace catalog (generated) |
87+
| `.mcp.json` | Shared MCP server config (HTTP transport; used by Claude Code and Codex) |
9488
| `gemini-extension.json` | Gemini CLI extension manifest (generated) |
9589
| `skills/klarity-process-context-graph/SKILL.md` | The Klarity skill prompt |
9690
| `tests/test_packaging.py` | Manifest invariants + drift checks |

docs/concepts/discover-structure-improve.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ What lives here:
3333

3434
## Improve
3535

36-
Use [Advisor](/concepts/objectives) or this MCP in your AI tools to analyze thousands of processes at once and surface improvement opportunities. Signals provide individual-level feedback. Agents read in-flight transformation work through objective tools — picking up where prior runs left off rather than starting cold.
36+
Use [Advisor](/concepts/objectives) or this MCP in your AI tools to analyze thousands of processes at once surface improvement opportunities, drive transformation, and build skills and agents grounded in how your business actually runs.
3737

3838
What lives here:
3939

docs/install/codex.mdx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,18 @@ icon: "/images/icons/codex.svg"
1414
Sign-in will fail if you skip this. See [Authentication → Enable the MCP in your workspace](/authentication#enable-the-mcp-in-your-workspace) for the canonical reference, and [FAQ](/faq) for other common install issues.
1515
</Info>
1616

17-
Two install paths are supported. The first uses Codex's built-in OAuth login; the second uses an API key in your config file.
17+
Two install paths are supported. The plugin path is preferred — it ships the bundled skill prompt and uses OAuth.
1818

19-
## CLI install (OAuth)
19+
## Plugin install (preferred, OAuth)
2020

21-
From a terminal:
21+
From a Codex session:
2222

23-
```bash
24-
codex mcp add klarity --url https://architect-v2-api.klarity.ai/mcp
25-
```
26-
27-
Then log in:
28-
29-
```bash
30-
codex mcp login klarity
23+
```text
24+
/plugin marketplace add klarity-ai/klarity-mcp
25+
/plugin install klarity@klarity
3126
```
3227

33-
You'll be redirected to Klarity in your browser to authenticate.
28+
The first request that hits a Klarity tool will prompt you to sign in.
3429

3530
## Config file install (API key)
3631

klarity_mcp/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
CLAUDE_MARKETPLACE_PATH,
55
CLAUDE_MCP_CONFIG_PATH,
66
CLAUDE_PLUGIN_DIR,
7+
CODEX_MARKETPLACE_PATH,
8+
CODEX_PLUGIN_DIR,
79
GEMINI_EXTENSION_PATH,
810
KLARITY_MCP_METADATA,
911
KlarityMCPMetadata,
@@ -15,6 +17,8 @@
1517
"CLAUDE_MARKETPLACE_PATH",
1618
"CLAUDE_MCP_CONFIG_PATH",
1719
"CLAUDE_PLUGIN_DIR",
20+
"CODEX_MARKETPLACE_PATH",
21+
"CODEX_PLUGIN_DIR",
1822
"GEMINI_EXTENSION_PATH",
1923
"KLARITY_MCP_METADATA",
2024
"KlarityMCPMetadata",

klarity_mcp/builders.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
CLAUDE_MARKETPLACE_PATH,
1111
CLAUDE_MCP_CONFIG_PATH,
1212
CLAUDE_PLUGIN_DIR,
13+
CODEX_MARKETPLACE_PATH,
14+
CODEX_PLUGIN_DIR,
1315
GEMINI_EXTENSION_PATH,
1416
KLARITY_MCP_METADATA,
1517
KlarityMCPMetadata,
@@ -95,6 +97,81 @@ def build_claude_mcp_config(
9597
}
9698

9799

100+
def build_codex_plugin_manifest(
101+
metadata: KlarityMCPMetadata = KLARITY_MCP_METADATA,
102+
) -> dict[str, Any]:
103+
"""Output: .codex-plugin/plugin.json (Codex plugin manifest).
104+
105+
Mirrors Claude's `mcpServers: "./.mcp.json"` pointer so a single shared
106+
`.mcp.json` at the repo root serves both clients. The `interface` block
107+
drives the in-CLI presentation Codex renders during install.
108+
"""
109+
return {
110+
"name": metadata.plugin_name,
111+
"version": metadata.plugin_version,
112+
"description": metadata.plugin_description,
113+
"author": {
114+
"name": metadata.author_name,
115+
"email": metadata.author_email,
116+
"url": metadata.author_url,
117+
},
118+
"homepage": metadata.homepage_url,
119+
"repository": metadata.repository_url,
120+
"license": metadata.license_spdx,
121+
"keywords": list(metadata.keywords),
122+
"skills": "./skills/",
123+
"mcpServers": "./.mcp.json",
124+
"interface": {
125+
"displayName": metadata.app_display_name,
126+
"shortDescription": metadata.interface_short_description,
127+
"longDescription": metadata.interface_long_description,
128+
"developerName": metadata.author_name,
129+
"category": metadata.category,
130+
"capabilities": list(metadata.capabilities),
131+
"websiteURL": metadata.homepage_url,
132+
"privacyPolicyURL": metadata.privacy_policy_url,
133+
"termsOfServiceURL": metadata.terms_of_service_url,
134+
"defaultPrompt": list(metadata.default_prompts),
135+
"brandColor": metadata.brand_color,
136+
},
137+
}
138+
139+
140+
def build_codex_marketplace_manifest(
141+
metadata: KlarityMCPMetadata = KLARITY_MCP_METADATA,
142+
) -> dict[str, Any]:
143+
"""Output: .agents/plugins/marketplace.json (Codex marketplace catalog).
144+
145+
Codex's marketplace loader probes `.agents/plugins/marketplace.json` first
146+
(`codex-rs/core-plugins/src/marketplace.rs`). Source paths in this manifest
147+
must stay inside the marketplace root, so we can't `..` up to the
148+
`.codex-plugin/plugin.json` at the repo root with a local source. The
149+
cleanest "repo IS the plugin" mapping is a remote `url` source pointing
150+
back at the same repository — Codex clones it and finds the plugin
151+
manifest at the root.
152+
"""
153+
return {
154+
"name": metadata.plugin_name,
155+
"interface": {
156+
"displayName": metadata.app_display_name,
157+
},
158+
"plugins": [
159+
{
160+
"name": metadata.plugin_name,
161+
"source": {
162+
"source": "url",
163+
"url": f"{metadata.repository_url}.git",
164+
},
165+
"policy": {
166+
"installation": "AVAILABLE",
167+
"authentication": "ON_INSTALL",
168+
},
169+
"category": metadata.category,
170+
}
171+
],
172+
}
173+
174+
98175
def build_gemini_extension_manifest(
99176
metadata: KlarityMCPMetadata = KLARITY_MCP_METADATA,
100177
) -> dict[str, Any]:
@@ -122,6 +199,8 @@ def build_manifest_payloads(
122199
CLAUDE_PLUGIN_DIR / "plugin.json": build_claude_plugin_manifest(metadata),
123200
CLAUDE_MARKETPLACE_PATH: build_claude_marketplace_manifest(metadata),
124201
CLAUDE_MCP_CONFIG_PATH: build_claude_mcp_config(metadata),
202+
CODEX_PLUGIN_DIR / "plugin.json": build_codex_plugin_manifest(metadata),
203+
CODEX_MARKETPLACE_PATH: build_codex_marketplace_manifest(metadata),
125204
GEMINI_EXTENSION_PATH: build_gemini_extension_manifest(metadata),
126205
}
127206

klarity_mcp/metadata.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class KlarityMCPMetadata:
5858
terms_of_service_url="https://www.klarity.ai/terms-of-service-2025",
5959
support_email="hello@klarity.ai",
6060
category="Business",
61-
brand_color="#2F6BFF",
61+
brand_color="#FF6C14",
6262
mcp_server_key="klarity",
6363
mcp_url="https://architect-v2-api.klarity.ai/mcp",
6464
oauth_resource="https://architect-v2-api.klarity.ai",
@@ -101,4 +101,13 @@ class KlarityMCPMetadata:
101101
# `/plugin install <plugin>@<owner>/<repo>` to resolve this repo as a marketplace.
102102
CLAUDE_MARKETPLACE_PATH = CLAUDE_PLUGIN_DIR / "marketplace.json"
103103
GEMINI_EXTENSION_PATH = REPO_ROOT / "gemini-extension.json"
104+
# Codex plugin manifest lives at `.codex-plugin/plugin.json` per OpenAI's Codex
105+
# plugin spec (mirrors Claude's `.claude-plugin/plugin.json`). The Codex loader
106+
# also resolves `mcpServers: "./.mcp.json"` from the plugin root, so the shared
107+
# `.mcp.json` at the repo root works for both Claude and Codex.
108+
CODEX_PLUGIN_DIR = REPO_ROOT / ".codex-plugin"
109+
# Codex's marketplace lookup (codex-rs/core-plugins/src/marketplace.rs) probes
110+
# `.agents/plugins/marketplace.json` first, then `.claude-plugin/marketplace.json`
111+
# as a fallback. The Codex-native location avoids ambiguity with Claude's schema.
112+
CODEX_MARKETPLACE_PATH = REPO_ROOT / ".agents" / "plugins" / "marketplace.json"
104113
SKILLS_DIR = REPO_ROOT / "skills"

tests/test_packaging.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from klarity_mcp import (
1212
CLAUDE_PLUGIN_DIR,
13+
CODEX_PLUGIN_DIR,
1314
GEMINI_EXTENSION_PATH,
1415
KLARITY_MCP_METADATA,
1516
REPO_ROOT,
@@ -19,6 +20,8 @@
1920
build_claude_marketplace_manifest,
2021
build_claude_mcp_config,
2122
build_claude_plugin_manifest,
23+
build_codex_marketplace_manifest,
24+
build_codex_plugin_manifest,
2225
build_gemini_extension_manifest,
2326
build_manifest_texts,
2427
render_manifest,
@@ -102,6 +105,84 @@ def test_claude_marketplace_has_plugin_with_resolvable_source() -> None:
102105
)
103106

104107

108+
def test_codex_plugin_manifest_required_metadata() -> None:
109+
payload = build_codex_plugin_manifest(KLARITY_MCP_METADATA)
110+
for key in ("name", "version", "description", "homepage", "repository", "license", "keywords"):
111+
assert payload.get(key), f"Codex plugin.json is missing required field: {key}"
112+
assert payload["name"] == KLARITY_MCP_METADATA.plugin_name
113+
interface = payload["interface"]
114+
for key in ("displayName", "shortDescription", "longDescription", "category", "brandColor", "defaultPrompt"):
115+
assert interface.get(key), f"Codex plugin.json interface missing: {key}"
116+
# Codex caps defaultPrompt to 3 entries (extras are dropped silently).
117+
assert len(interface["defaultPrompt"]) <= 3, (
118+
"Codex truncates `defaultPrompt` past 3 entries; keep the canonical list <= 3 "
119+
"so the rendered prompts match what's declared."
120+
)
121+
122+
123+
def test_codex_plugin_mcp_servers_path_resolves_to_real_file() -> None:
124+
"""Codex resolves `mcpServers` from the plugin root, same as Claude. Mirror
125+
the runtime-fidelity test we already have for Claude.
126+
"""
127+
plugin_manifest_path = CODEX_PLUGIN_DIR / "plugin.json"
128+
plugin_root = plugin_manifest_path.parent.parent
129+
manifest = json.loads(plugin_manifest_path.read_text())
130+
mcp_ref = manifest["mcpServers"]
131+
assert isinstance(mcp_ref, str)
132+
mcp_path = (plugin_root / mcp_ref.removeprefix("./")).resolve()
133+
assert mcp_path.exists(), (
134+
f"Codex manifest.mcpServers points to {mcp_ref!r}, which resolves to "
135+
f"{mcp_path}, but no file is committed there."
136+
)
137+
138+
139+
def test_codex_plugin_skills_path_resolves_to_real_dir() -> None:
140+
"""Codex resolves `skills` from the plugin root. The bundled skill must
141+
actually exist there or `/plugin install` ships a plugin with no skills.
142+
"""
143+
plugin_manifest_path = CODEX_PLUGIN_DIR / "plugin.json"
144+
plugin_root = plugin_manifest_path.parent.parent
145+
manifest = json.loads(plugin_manifest_path.read_text())
146+
skills_ref = manifest["skills"]
147+
assert isinstance(skills_ref, str)
148+
skills_path = (plugin_root / skills_ref.removeprefix("./")).resolve()
149+
assert skills_path.exists() and skills_path.is_dir(), (
150+
f"Codex manifest.skills points to {skills_ref!r}, which resolves to "
151+
f"{skills_path}, but no directory is committed there."
152+
)
153+
154+
155+
def test_codex_marketplace_has_plugin_with_supported_source_shape() -> None:
156+
"""Codex's marketplace loader accepts string-local, object-local, `url`,
157+
and `git-subdir` source shapes (per openai/codex PR #18017). We use the
158+
`url` shape so the entire repo can act as the plugin without duplicating
159+
the manifest under `.agents/plugins/`. Guard the shape so the file stays
160+
parseable.
161+
"""
162+
payload = build_codex_marketplace_manifest(KLARITY_MCP_METADATA)
163+
assert payload["name"] == KLARITY_MCP_METADATA.plugin_name
164+
assert isinstance(payload["plugins"], list) and payload["plugins"]
165+
for plugin in payload["plugins"]:
166+
assert plugin.get("name"), "marketplace plugin must have a name"
167+
source = plugin.get("source")
168+
assert isinstance(source, dict), (
169+
"Codex marketplace source must be an object (string-local form is allowed "
170+
"but we use the explicit object form for the repo-as-plugin pattern)"
171+
)
172+
kind = source.get("source")
173+
assert kind in {"local", "url", "git-subdir"}, (
174+
f"unsupported source kind {kind!r}; must be local/url/git-subdir"
175+
)
176+
if kind == "url":
177+
assert source.get("url", "").startswith("https://"), (
178+
"url-shaped sources must point at an https git URL"
179+
)
180+
policy = plugin.get("policy")
181+
assert isinstance(policy, dict)
182+
assert policy.get("installation") in {"AVAILABLE", "NOT_AVAILABLE", "INSTALLED_BY_DEFAULT"}
183+
assert policy.get("authentication") in {"ON_INSTALL", "ON_USE"}
184+
185+
105186
def test_gemini_extension_name_matches_canonical_plugin_name() -> None:
106187
payload = build_gemini_extension_manifest(KLARITY_MCP_METADATA)
107188
assert payload["name"] == KLARITY_MCP_METADATA.plugin_name, (

0 commit comments

Comments
 (0)