Package the plugin for Codex from the same bundle
The Claude Code plugin moves to plugins/haiku-rag/ and gains a Codex manifest at .codex-plugin/plugin.json; both share one .mcp.json and one haiku-rag Agent Skill. .agents/plugins/marketplace.json serves Codex, the Claude marketplace points at the new path, and scripts/bump_version.py bumps both manifests. The skill declares its compatibility.
This commit is contained in:
parent
110adf23ee
commit
d7c131bc28
12 changed files with 131 additions and 35 deletions
20
.agents/plugins/marketplace.json
Normal file
20
.agents/plugins/marketplace.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "haiku-rag",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "haiku.rag"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "haiku-rag",
|
||||||
|
"source": {
|
||||||
|
"source": "local",
|
||||||
|
"path": "./plugins/haiku-rag"
|
||||||
|
},
|
||||||
|
"policy": {
|
||||||
|
"installation": "AVAILABLE",
|
||||||
|
"authentication": "ON_INSTALL"
|
||||||
|
},
|
||||||
|
"category": "Productivity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "haiku-rag",
|
"name": "haiku-rag",
|
||||||
"source": "./claude-plugin",
|
"source": "./plugins/haiku-rag",
|
||||||
"description": "Search, read and question your haiku.rag knowledge base from Claude Code."
|
"description": "Search, read and analyze your haiku.rag knowledge base from Claude Code."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Claude Code plugin under `claude-plugin/`: the server configuration and the
|
- Claude Code and Codex plugin under `plugins/haiku-rag/`: two client manifests
|
||||||
`haiku-rag` skill. `claude plugin marketplace add ggozad/haiku.rag`, then
|
sharing the server configuration and the `haiku-rag` Agent Skill.
|
||||||
`claude plugin install haiku-rag`.
|
|
||||||
- MCP tool `execute_code(code, filter, sources)`: runs a program in the
|
- MCP tool `execute_code(code, filter, sources)`: runs a program in the
|
||||||
analysis sandbox over the selected documents and returns what it printed;
|
analysis sandbox over the selected documents and returns what it printed;
|
||||||
one sandbox per call.
|
one sandbox per call.
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -110,7 +110,7 @@ For direct agent composition, see the [capabilities documentation](https://ggoza
|
||||||
|
|
||||||
## MCP Server
|
## MCP Server
|
||||||
|
|
||||||
Use with AI assistants like Claude Code and Claude Desktop:
|
Use with AI assistants like Claude Code, Codex, and Claude Desktop:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
haiku-rag mcp --stdio
|
haiku-rag mcp --stdio
|
||||||
|
|
@ -123,6 +123,13 @@ claude plugin marketplace add ggozad/haiku.rag
|
||||||
claude plugin install haiku-rag
|
claude plugin install haiku-rag
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In Codex, install the same plugin from its marketplace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codex plugin marketplace add ggozad/haiku.rag
|
||||||
|
codex plugin add haiku-rag@haiku-rag
|
||||||
|
```
|
||||||
|
|
||||||
Add to your Claude Desktop configuration:
|
Add to your Claude Desktop configuration:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
@ -136,7 +143,7 @@ Add to your Claude Desktop configuration:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Provides search, document, QA, and analysis tools directly in your AI assistant.
|
Provides search, document reading, and analysis tools directly in your AI assistant.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
||||||
30
docs/mcp.md
30
docs/mcp.md
|
|
@ -57,10 +57,34 @@ plugin:
|
||||||
claude mcp add haiku-rag -- haiku-rag mcp --stdio
|
claude mcp add haiku-rag -- haiku-rag mcp --stdio
|
||||||
```
|
```
|
||||||
|
|
||||||
The skill works with that registration too: copy `claude-plugin/skills/haiku-rag`
|
The skill works with that registration too: copy `plugins/haiku-rag/skills/haiku-rag`
|
||||||
into `~/.claude/skills/` and change the tool prefix in its `allowed-tools` from
|
into `~/.claude/skills/` and change the tool prefix in its `allowed-tools` from
|
||||||
`mcp__plugin_haiku-rag_haiku-rag__` to `mcp__haiku-rag__`.
|
`mcp__plugin_haiku-rag_haiku-rag__` to `mcp__haiku-rag__`.
|
||||||
|
|
||||||
|
## Codex
|
||||||
|
|
||||||
|
The repository's Codex plugin registers the server and installs the same Agent
|
||||||
|
Skill:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codex plugin marketplace add ggozad/haiku.rag
|
||||||
|
codex plugin add haiku-rag@haiku-rag
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugin runs `haiku-rag mcp --stdio`, so `haiku-rag` must be on the PATH.
|
||||||
|
Invoke the skill as `$haiku-rag`. Codex can also select it automatically from
|
||||||
|
its description. To register the server without the plugin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codex mcp add haiku-rag -- haiku-rag mcp --stdio
|
||||||
|
```
|
||||||
|
|
||||||
|
The skill works with that registration too: copy
|
||||||
|
`plugins/haiku-rag/skills/haiku-rag` into `~/.agents/skills/`.
|
||||||
|
The `allowed-tools` field supplies Claude Code's tool pre-approval and may be
|
||||||
|
ignored by other Agent Skills clients. Codex configures MCP tool approvals
|
||||||
|
separately in `config.toml`.
|
||||||
|
|
||||||
## Claude Desktop Integration
|
## Claude Desktop Integration
|
||||||
|
|
||||||
Add to your Claude Desktop configuration (`claude_desktop_config.json`):
|
Add to your Claude Desktop configuration (`claude_desktop_config.json`):
|
||||||
|
|
@ -168,8 +192,8 @@ traceback goes to the server log.
|
||||||
|
|
||||||
The server publishes `instructions` describing the knowledge base: what it
|
The server publishes `instructions` describing the knowledge base: what it
|
||||||
holds, when to reach for it, the collection names when it covers several, and
|
holds, when to reach for it, the collection names when it covers several, and
|
||||||
`prompts.domain_preamble` when set. Claude Code shows them to the model. Claude
|
`prompts.domain_preamble` when set. Claude Code and Codex show them to the
|
||||||
Desktop does not, so every tool description stands on its own.
|
model. Claude Desktop does not, so every tool description stands on its own.
|
||||||
|
|
||||||
## Continuous ingestion
|
## Continuous ingestion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "haiku-rag",
|
"name": "haiku-rag",
|
||||||
"version": "0.82.1",
|
"version": "0.82.1",
|
||||||
"description": "Search, read and question your haiku.rag knowledge base from Claude Code.",
|
"description": "Search, read and analyze your haiku.rag knowledge base from Claude Code.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Yiorgis Gozadinos",
|
"name": "Yiorgis Gozadinos",
|
||||||
"email": "ggozadinos@gmail.com"
|
"email": "ggozadinos@gmail.com"
|
||||||
26
plugins/haiku-rag/.codex-plugin/plugin.json
Normal file
26
plugins/haiku-rag/.codex-plugin/plugin.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "haiku-rag",
|
||||||
|
"version": "0.82.1",
|
||||||
|
"description": "Search, read and analyze your haiku.rag knowledge base from Codex.",
|
||||||
|
"author": {
|
||||||
|
"name": "Yiorgis Gozadinos",
|
||||||
|
"email": "ggozadinos@gmail.com",
|
||||||
|
"url": "https://github.com/ggozad"
|
||||||
|
},
|
||||||
|
"homepage": "https://ggozad.github.io/haiku.rag/mcp/",
|
||||||
|
"repository": "https://github.com/ggozad/haiku.rag",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": ["rag", "knowledge-base", "search", "documents", "mcp"],
|
||||||
|
"skills": "./skills/",
|
||||||
|
"mcpServers": "./.mcp.json",
|
||||||
|
"interface": {
|
||||||
|
"displayName": "haiku.rag",
|
||||||
|
"shortDescription": "Search and analyze your haiku.rag knowledge base",
|
||||||
|
"longDescription": "Search, read, and compute over documents in your local haiku.rag knowledge base through MCP tools.",
|
||||||
|
"developerName": "Yiorgis Gozadinos",
|
||||||
|
"category": "Productivity",
|
||||||
|
"capabilities": ["Interactive", "Read"],
|
||||||
|
"websiteURL": "https://ggozad.github.io/haiku.rag/",
|
||||||
|
"defaultPrompt": "Search my haiku.rag knowledge base and cite the relevant documents."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ description: Search, read and compute over the user's haiku.rag knowledge base
|
||||||
from the user's ingested documents, when asked to find, look up, check or
|
from the user's ingested documents, when asked to find, look up, check or
|
||||||
cite something in their documents or knowledge base, or when the question is
|
cite something in their documents or knowledge base, or when the question is
|
||||||
about the user's own material rather than general knowledge.
|
about the user's own material rather than general knowledge.
|
||||||
|
compatibility: Requires the haiku-rag MCP server to be registered in the client.
|
||||||
allowed-tools:
|
allowed-tools:
|
||||||
- mcp__plugin_haiku-rag_haiku-rag__search_documents
|
- mcp__plugin_haiku-rag_haiku-rag__search_documents
|
||||||
- mcp__plugin_haiku-rag_haiku-rag__search_documents_by_image
|
- mcp__plugin_haiku-rag_haiku-rag__search_documents_by_image
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
"""
|
"""
|
||||||
Version bumping script for haiku.rag workspace.
|
Version bumping script for haiku.rag workspace.
|
||||||
|
|
||||||
Updates version in all pyproject.toml files, the Claude Code plugin manifest
|
Updates version in all pyproject.toml files, both plugin manifests, and
|
||||||
and CHANGELOG.md.
|
CHANGELOG.md.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
@ -56,7 +56,7 @@ def update_example_dependencies(file_path: Path, new_version: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def update_plugin_version(file_path: Path, new_version: str) -> None:
|
def update_plugin_version(file_path: Path, new_version: str) -> None:
|
||||||
"""Update the version in the Claude Code plugin manifest."""
|
"""Update the version in a plugin manifest."""
|
||||||
content = file_path.read_text()
|
content = file_path.read_text()
|
||||||
updated = re.sub(
|
updated = re.sub(
|
||||||
r'^(\s*"version": )"[^"]+"',
|
r'^(\s*"version": )"[^"]+"',
|
||||||
|
|
@ -136,12 +136,15 @@ def main():
|
||||||
root / "app" / "backend" / "pyproject.toml",
|
root / "app" / "backend" / "pyproject.toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
plugin_file = root / "claude-plugin" / ".claude-plugin" / "plugin.json"
|
plugin_files = [
|
||||||
|
root / "plugins" / "haiku-rag" / ".claude-plugin" / "plugin.json",
|
||||||
|
root / "plugins" / "haiku-rag" / ".codex-plugin" / "plugin.json",
|
||||||
|
]
|
||||||
changelog_file = root / "CHANGELOG.md"
|
changelog_file = root / "CHANGELOG.md"
|
||||||
|
|
||||||
# Check all files exist
|
# Check all files exist
|
||||||
for file in (
|
for file in (
|
||||||
pyproject_files + example_pyproject_files + [plugin_file, changelog_file]
|
pyproject_files + example_pyproject_files + plugin_files + [changelog_file]
|
||||||
):
|
):
|
||||||
if not file.exists():
|
if not file.exists():
|
||||||
print(f"Error: {file} not found")
|
print(f"Error: {file} not found")
|
||||||
|
|
@ -172,7 +175,8 @@ def main():
|
||||||
for file in example_pyproject_files:
|
for file in example_pyproject_files:
|
||||||
update_example_dependencies(file, new_version)
|
update_example_dependencies(file, new_version)
|
||||||
|
|
||||||
update_plugin_version(plugin_file, new_version)
|
for file in plugin_files:
|
||||||
|
update_plugin_version(file, new_version)
|
||||||
|
|
||||||
# Update CHANGELOG.md
|
# Update CHANGELOG.md
|
||||||
update_changelog(changelog_file, new_version)
|
update_changelog(changelog_file, new_version)
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,15 @@ def test_update_plugin_version_rewrites_only_the_version_field(tmp_path, monkeyp
|
||||||
assert manifest.read_text().endswith("}\n")
|
assert manifest.read_text().endswith("}\n")
|
||||||
|
|
||||||
|
|
||||||
def test_the_shipped_plugin_manifest_carries_the_package_version():
|
def test_the_shipped_plugin_manifests_carry_the_package_version():
|
||||||
root = Path(__file__).resolve().parents[1]
|
root = Path(__file__).resolve().parents[1]
|
||||||
plugin = json.loads(
|
package_version = bump_version.get_current_version(
|
||||||
(root / "claude-plugin" / ".claude-plugin" / "plugin.json").read_text()
|
|
||||||
)
|
|
||||||
|
|
||||||
assert plugin["version"] == bump_version.get_current_version(
|
|
||||||
root / "haiku_rag_slim" / "pyproject.toml"
|
root / "haiku_rag_slim" / "pyproject.toml"
|
||||||
)
|
)
|
||||||
|
for client in ("claude", "codex"):
|
||||||
|
manifest = json.loads(
|
||||||
|
(
|
||||||
|
root / "plugins" / "haiku-rag" / f".{client}-plugin" / "plugin.json"
|
||||||
|
).read_text()
|
||||||
|
)
|
||||||
|
assert manifest["version"] == package_version
|
||||||
|
|
|
||||||
|
|
@ -1158,27 +1158,39 @@ class TestMCPErrorContract:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestClaudeCodePlugin:
|
class TestAgentPlugins:
|
||||||
"""The plugin under claude-plugin/ points at the server this module builds."""
|
"""The shared Claude Code and Codex plugin points at this MCP server."""
|
||||||
|
|
||||||
root = Path(__file__).resolve().parents[1]
|
root = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
def test_the_manifests_name_the_plugin_and_its_server(self):
|
def test_the_manifests_name_the_plugin_and_its_server(self):
|
||||||
import json
|
import json
|
||||||
|
|
||||||
plugin = json.loads(
|
claude_plugin = json.loads(
|
||||||
(self.root / "claude-plugin/.claude-plugin/plugin.json").read_text()
|
(self.root / "plugins/haiku-rag/.claude-plugin/plugin.json").read_text()
|
||||||
)
|
)
|
||||||
marketplace = json.loads(
|
codex_plugin = json.loads(
|
||||||
|
(self.root / "plugins/haiku-rag/.codex-plugin/plugin.json").read_text()
|
||||||
|
)
|
||||||
|
claude_marketplace = json.loads(
|
||||||
(self.root / ".claude-plugin/marketplace.json").read_text()
|
(self.root / ".claude-plugin/marketplace.json").read_text()
|
||||||
)
|
)
|
||||||
servers = json.loads((self.root / "claude-plugin/.mcp.json").read_text())
|
codex_marketplace = json.loads(
|
||||||
|
(self.root / ".agents/plugins/marketplace.json").read_text()
|
||||||
|
)
|
||||||
|
servers = json.loads((self.root / "plugins/haiku-rag/.mcp.json").read_text())
|
||||||
|
|
||||||
assert plugin["name"] == "haiku-rag"
|
assert claude_plugin["name"] == codex_plugin["name"] == "haiku-rag"
|
||||||
assert plugin["description"]
|
assert claude_plugin["description"]
|
||||||
[entry] = marketplace["plugins"]
|
assert codex_plugin["description"]
|
||||||
assert entry["name"] == plugin["name"]
|
assert codex_plugin["skills"] == "./skills/"
|
||||||
assert entry["source"] == "./claude-plugin"
|
assert codex_plugin["mcpServers"] == "./.mcp.json"
|
||||||
|
[claude_entry] = claude_marketplace["plugins"]
|
||||||
|
assert claude_entry["name"] == claude_plugin["name"]
|
||||||
|
assert claude_entry["source"] == "./plugins/haiku-rag"
|
||||||
|
[codex_entry] = codex_marketplace["plugins"]
|
||||||
|
assert codex_entry["name"] == codex_plugin["name"]
|
||||||
|
assert codex_entry["source"]["path"] == "./plugins/haiku-rag"
|
||||||
assert servers["mcpServers"]["haiku-rag"]["args"] == ["mcp", "--stdio"]
|
assert servers["mcpServers"]["haiku-rag"]["args"] == ["mcp", "--stdio"]
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|
@ -1187,7 +1199,7 @@ class TestClaudeCodePlugin:
|
||||||
):
|
):
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
text = (self.root / "claude-plugin/skills/haiku-rag/SKILL.md").read_text()
|
text = (self.root / "plugins/haiku-rag/skills/haiku-rag/SKILL.md").read_text()
|
||||||
_, frontmatter, _ = text.split("---", 2)
|
_, frontmatter, _ = text.split("---", 2)
|
||||||
skill = yaml.safe_load(frontmatter)
|
skill = yaml.safe_load(frontmatter)
|
||||||
prefix = "mcp__plugin_haiku-rag_haiku-rag__"
|
prefix = "mcp__plugin_haiku-rag_haiku-rag__"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue