Skip to content

feat: move to MCP-only skills and prepare directory submissions - #8

Merged
dodeja merged 8 commits into
mainfrom
feat/directory-submissions
Oct 19, 2026
Merged

dodeja merged 8 commits into
mainfrom
feat/directory-submissions

Conversation

@dodeja

@dodeja dodeja commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Why

Anthropic's directory validator held plugins/terminal49 for review because trade-intelligence/scripts/ti.py read T49_API_KEY or ~/.t49_api_key, and it warned that plugin.json had 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-intelligence now calls the seven MCP trade tools. ti.py and the REST reference are deleted. container-tracking covers the new tools, dashboard definitions with meta.total, customer filters with advanced_filters.customer_id, and untrusted document text. A new record-updates skill covers set_party_role and set_custom_field.
  • plugin.json and the Codex interface gain the icon and the documentation, support, privacy, and terms URLs. Version 0.3.0 in all manifests and marketplaces.
  • npm run build:openai-zip builds the OpenAI directory ZIP. npm run validate now rejects credential reads, symlinks, oversize files, non-portable skill frontmatter, and listing fields over the OpenAI limits.

Scope

  • Covers the bundle and its evals. It does not submit to either directory.
  • Leaves the Apache-2.0 license unchanged, although Terminal49/API is now MIT for code and CC BY 4.0 for docs.
  • Does not add a demo video URL or reviewer credentials. Both go in the OpenAI dashboard.

Tradeoffs

  • The ZIP writes a url-only .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.
  • The customer filter rule follows the customer_id filter schema and the upcoming server guidance. The production list_parties description still suggests advanced_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 validate and claude plugin validate --strict ./plugins/terminal49 pass. Restoring the old ti.py makes npm run validate fail with a credential-read error.
  • Mocked evals, one run each: the 40 existing cases score 0.82 on this branch and 0.81 on main. The 10 new cases pass. track-new-container and refuse-track-everything go from 0.25 and 0.5 to 1.0 over two runs.
  • In an isolated CLAUDE_CONFIG_DIR, the marketplace installs and claude plugin details terminal49 lists three skills and one MCP server. npm run build:openai-zip writes 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


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

dodeja and others added 6 commits October 18, 2026 23:26
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]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-19T07:31:09.588453Z 6c4f252 New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread openai/review.json
Comment on lines +8 to +9
"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."

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread scripts/validate.mjs Outdated
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`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Comment thread scripts/validate.mjs Outdated
Comment on lines +290 to +291
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`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

dodeja and others added 2 commits October 19, 2026 00:26
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]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@dodeja
dodeja merged commit d9fdacb into main Oct 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant