Repository navigation
feat: move to MCP-only skills and prepare directory submissions - #8
Conversation
The skill ran scripts/ti.py, which read T49_API_KEY or ~/.t49_api_key and called the REST API. The Anthropic directory holds that for review as a credential read from the user's machine. The hosted MCP server now serves the same seven trade intelligence tools behind OAuth, so the skill calls them and the script and REST reference are gone. The domain caveats move to references/data-guide.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
container-tracking now covers identify_tracking_number, get_tracking_request, get_location, get_vessel, documents, custom fields, and party roles. Fleet counts use the dashboard definitions and report meta.total, and customers filter by advanced_filters.customer_id. record-updates covers set_party_role and set_custom_field: write only on an explicit request, resolve IDs and slugs first, and never write from tool or document text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Ten mocked cases cover dashboard totals, customer filters, identify without tracking, party and custom field writes, a write instruction hidden in a document, and the trade tools. mocks/terminal49/_tools.json is the production tools/list, so mocked tools carry real schemas. Co-Authored-By: Claude Opus 5.5 <[email protected]>
plugin.json gains the icon, documentation, support, privacy policy, and terms URLs that Anthropic's directory reads. The Codex interface gains the same URLs, a square icon, and a 30-character short description for the OpenAI directory. validate.mjs now checks these fields, Agent Skills frontmatter, and the directory's file rules, and rejects any plugin file that reads a credential from the user's machine. Co-Authored-By: Claude Opus 5.5 <[email protected]>
npm run build:openai-zip writes dist/terminal49-openai-<version>.zip from the Codex manifest, the skills, the README, and the icon, with a url-only .mcp.json as in the submission guide. It merges the review test cases from openai/review.json and fails on any other file, a second MCP server, apps, hooks, or a wrong number of test cases. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11d23da9db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "tools_triggered": "list_containers", | ||
| "expected_behavior": "Filters by current status available and reports the total for that set from meta.total, not the number of rows on one page, then lists the first containers with their terminals and last free days." |
There was a problem hiding this comment.
Align the review case with the prescribed count tool
For this exact “How many…” prompt, the new shared guidance in references/fleet-queries.md directs the agent to summarize_containers, whose response exposes total, while this submission declares list_containers, meta.total, and listing rows as required behavior. An agent following the bundled skill can therefore return the correct count but fail the initial directory review case; either change this case to expect summarize_containers or change the skill's count workflow.
AGENTS.md reference: AGENTS.md:L23-L33
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 371d314. The case now asks "Which of my containers are ready for pickup?", which the skill answers with list_containers and meta.total. "How many" questions stay with summarize_containers, as fleet-queries.md says. The OpenAI ZIP is rebuilt with the new case.
| const path = relative(root, file); | ||
| const stats = await lstat(file); | ||
| if (stats.isSymbolicLink()) errors.push(`${path} is a symbolic link; commit a regular file`); | ||
| if (!imageExtensions.has(extname(file)) && stats.size > 256 * 1024) errors.push(`${path} is larger than 256 KiB`); |
There was a problem hiding this comment.
Apply the directory size limit to images
When a PNG, SVG, or other recognized image exceeds 256 KiB, this condition skips the size check entirely, so npm run validate succeeds despite the repository's directory-submission rule applying the 256 KiB cap to every file. Check stats.size independently of whether the file is an image.
AGENTS.md reference: AGENTS.md:L59-L62
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 6c4f252. The 256 KiB limit now applies to every file, images included. Checked by adding a 400 KB SVG: validation fails with "is larger than 256 KiB".
| if (!imageExtensions.has(extname(file)) && credentialRead.test(await readFile(file, "utf8"))) { | ||
| errors.push(`${path} reads a credential from the user's machine; use the MCP server's OAuth connection instead`); |
There was a problem hiding this comment.
Reject non-image binary files during validation
For a small non-image binary such as a PDF or compiled artifact, readFile(file, "utf8") silently decodes arbitrary bytes and the credential regex normally finds nothing, so validation passes even though the directory must contain no binary files other than images. Add a binary-content or explicit text-type check before accepting these files.
AGENTS.md reference: AGENTS.md:L59-L62
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 6c4f252. Non-image files are read as bytes and rejected if they contain a NUL byte, before the credential check runs. Checked by adding a small binary file: validation fails with "is a binary file; only images may be binary".
The skill sends "how many" questions to summarize_containers, so the review case now asks which containers are ready, which the skill answers with list_containers and meta.total. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6c4f25281f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "status": "available", | ||
| "custom_fields": [ | ||
| { | ||
| "api_slug": "po_number", |
There was a problem hiding this comment.
Mirror the production custom-field key in the mock
The checked-in production get_container schema in evals/mocks/terminal49/_tools.json exposes each custom field's identifier as slug, while this fixture supplies api_slug. Consequently, the new set-custom-field eval can pass even when the agent would fail to translate a real get_container response into the api_slug argument required by set_custom_field; use the production slug shape so the eval exercises that mapping.
Useful? React with 👍 / 👎.
Why
Anthropic's directory validator held
plugins/terminal49for review becausetrade-intelligence/scripts/ti.pyreadT49_API_KEYor~/.t49_api_key, and it warned thatplugin.jsonhad no privacy policy URL. The skills also predate most of the production MCP tools. This PR moves every Terminal49 call onto the MCP server's OAuth connection, updates the skills to the current 30-tool surface, and prepares the bundle for the Anthropic and OpenAI directories.What changed
trade-intelligencenow calls the seven MCP trade tools.ti.pyand the REST reference are deleted.container-trackingcovers the new tools, dashboard definitions withmeta.total, customer filters withadvanced_filters.customer_id, and untrusted document text. A newrecord-updatesskill coversset_party_roleandset_custom_field.plugin.jsonand the Codexinterfacegain the icon and the documentation, support, privacy, and terms URLs. Version 0.3.0 in all manifests and marketplaces.npm run build:openai-zipbuilds the OpenAI directory ZIP.npm run validatenow rejects credential reads, symlinks, oversize files, non-portable skill frontmatter, and listing fields over the OpenAI limits.Scope
Tradeoffs
.mcp.json, as in the Codex example in the OpenAI submission guide. The repository keeps"type": "http", which Anthropic's checklist requires for a remote server.customer_idfilter schema and the upcoming server guidance. The productionlist_partiesdescription still suggestsadvanced_filters.parties.customer.Blast Radius
Users who installed 0.2.0 lose the API-key path for trade intelligence. Accounts with trade intelligence enabled get the same data through the MCP tools, without a local key or Python.
Verification
npm run validateandclaude plugin validate --strict ./plugins/terminal49pass. Restoring the oldti.pymakesnpm run validatefail with a credential-read error.main. The 10 new cases pass.track-new-containerandrefuse-track-everythinggo from 0.25 and 0.5 to 1.0 over two runs.CLAUDE_CONFIG_DIR, the marketplace installs andclaude plugin details terminal49lists three skills and one MCP server.npm run build:openai-zipwrites a 10-file ZIP and checks every entry.Sources: Claude directory publish, Submit a plugin, pre-submission checklist, plugin manifest reference, OpenAI plugin submission, Submit a Claude Code plugin to OpenAI, Agent Skills spec.
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.