Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- name: Validate agent skills and plugin packages
run: |
bun test scripts/clawhub-ignore-policy.test.ts scripts/claude-mcp-config-policy.test.ts scripts/public-skill-discovery-policy.test.ts
bun test scripts/clawhub-ignore-policy.test.ts scripts/claude-mcp-config-policy.test.ts scripts/openai-tool-annotation-policy.test.ts scripts/public-skill-discovery-policy.test.ts
bun scripts/validate-skills.ts

- name: Type check
Expand Down
26 changes: 24 additions & 2 deletions packages/mcp/src/tools/read-tool-annotations.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, test } from 'bun:test'
import { mkdtempSync, rmSync } from 'node:fs'
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { join, resolve } from 'node:path'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { SceneBridge } from '../bridge/scene-bridge'
Expand Down Expand Up @@ -100,6 +100,19 @@ const TOOL_POLICIES = [
] as const

const EXPECTED_TOOL_NAMES = TOOL_POLICIES.flatMap(({ tools }) => tools).toSorted()
const annotationPacket = JSON.parse(
readFileSync(
resolve(import.meta.dir, '../../../../plugin-evals/tool-annotation-justifications.json'),
'utf8',
),
) as {
required_hints: Array<'readOnlyHint' | 'destructiveHint' | 'openWorldHint'>
tools: Array<{
name: string
annotations: Record<'readOnlyHint' | 'destructiveHint' | 'openWorldHint', boolean>
justifications: Record<'readOnlyHint' | 'destructiveHint' | 'openWorldHint', string>
}>
}

describe('MCP tool annotations', () => {
test('classifies every registered tool for approval-aware clients', async () => {
Expand All @@ -123,6 +136,15 @@ describe('MCP tool annotations', () => {
expect(byName.get(name)?.annotations).toEqual(policy.annotations)
}
}

expect(annotationPacket.tools.map(({ name }) => name)).toEqual(EXPECTED_TOOL_NAMES)
for (const tool of annotationPacket.tools) {
const registeredAnnotations = byName.get(tool.name)?.annotations
for (const hint of annotationPacket.required_hints) {
expect(tool.annotations[hint]).toBe(registeredAnnotations?.[hint])
expect(tool.justifications[hint].trim().length).toBeGreaterThan(0)
}
}
} finally {
await client.close()
await server.close()
Expand Down
6 changes: 4 additions & 2 deletions plugin-evals/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Publishing evaluation fixtures

`publishing-cases.json` is the draft cross-skill review suite for a future OpenAI **With MCP** submission. It contains at least five positive cases with expected result shapes, plus three negative or refusal-boundary cases with explicit reasons the plugin must not complete the requested action. The suite is blocked until Pascal provisions a stable hosted MCP review environment, OAuth-compatible reviewer access, and named disposable fixtures that reviewers can use without internal context. Each standalone skill also bundles:
`publishing-cases.json` is the draft cross-skill review suite for a future OpenAI **With MCP** submission. It contains at least five positive cases with expected result shapes, plus three negative or refusal-boundary cases with explicit reasons the plugin must not complete the requested action. `tool-annotation-justifications.json` records the exact three required hint values and a non-empty justification for every hint on all 46 expected MCP tools. Repository policy tests check its exact inventory and shape, and the live MCP regression test checks its values against `tools/list`.

The suite remains blocked until Pascal confirms an authorized verified OpenAI publisher identity, completes portal-token domain verification, passes Scan Tools against the production hosted endpoint, provisions OAuth-compatible reviewer access, and names disposable fixtures that reviewers can use without internal context. Each standalone skill also bundles:

- `evals/evals.json` for task behavior;
- `evals/trigger-evals.json` for description routing, with at least five positive and three negative queries.

The shared suite lives outside `skills/` because it is release evidence rather than skill runtime content. These draft cases are not reproducible reviewer materials yet, and their presence does not mean a native host or OpenAI portal scan has passed them. Record those results separately after the blocked hosted-MCP prerequisites exist.
The shared suite and annotation packet live outside `skills/` because they are submission evidence rather than skill runtime content. This repository state is preparation only: the draft cases are not reproducible reviewer materials yet, and no portal scan, domain verification, publisher verification, submission, approval, or publication is represented. Record those results separately after the blocked hosted-MCP prerequisites exist.
13 changes: 9 additions & 4 deletions plugin-evals/publishing-cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@
"submission_route": "with_mcp",
"status": "blocked",
"blockers": [
"A verified OpenAI publisher or developer identity authorized to submit for Pascal has not been confirmed.",
"Pascal's submission domain has not been verified with the portal-generated /.well-known/openai-apps-challenge token.",
"Production hosted MCP endpoint has not passed OpenAI Scan Tools for this candidate.",
"OAuth-compatible reviewer access and reusable demo credentials without MFA, SMS, email confirmation, or private-network access are not prepared.",
"Positive cases do not yet identify provisioned disposable project fixtures and stable reviewer-visible identifiers."
],
"tool_annotation_validation": {
"status": "local_pass",
"checked_at": "2026-09-09",
"checked_at": "2026-09-10",
"registered_tools": 46,
"required_hints": ["readOnlyHint", "destructiveHint", "openWorldHint"],
"justification_packet": "plugin-evals/tool-annotation-justifications.json",
"evidence": [
"The live MCP tools/list enumeration test classified all 46 registered tools and failed on any unlisted or unannotated tool.",
"The complete @pascal-app/mcp suite passed 361 tests with 1407 assertions, and the package TypeScript build passed."
"The machine-readable packet records exact values and non-empty per-hint justifications for all 46 expected tools.",
"The repository policy validator rejects missing, unexpected, duplicate, reordered, or wrongly classified tools and missing, blank, or extra hint justifications.",
"The live MCP tools/list regression test requires the packet's exact inventory and annotation values to match the registered server tools.",
"The complete @pascal-app/mcp suite passed 361 tests with 1684 assertions, and the MCP dependency build passed."
],
"limitations": "Local enumeration and package checks do not establish production hosted-MCP behavior, OpenAI Scan Tools approval, reviewer access, submission, or listing."
"limitations": "This is local submission preparation only. It does not establish production hosted-MCP behavior, a portal Scan Tools run or approval, domain verification, verified publisher identity, reviewer access, submission, review, publication, or listing."
},
"cases": [
{
Expand Down
4 changes: 3 additions & 1 deletion plugin-evals/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ Draft release notes for a future Pascal agent skills 0.1.7 **With MCP** submissi

The plugin teaches ChatGPT and Codex to create, inspect, edit, validate, save, and hand off editable Pascal 3D scenes through a separately connected Pascal MCP server. It also includes a focused furniture-fit workflow that reports measured footprint evidence, unsupported checks, and one bounded next action without authorizing project changes or spending.

The source package contains two skills, portable Agent Plugins metadata, OpenAI listing metadata, and bundled square icons. Both skills require Pascal MCP tools for their tool-backed workflows, so they must not be submitted through the Skills only route. The With MCP submission remains blocked until the production endpoint passes Scan Tools, OAuth-compatible reviewer access and credentials exist, domain verification succeeds, and each positive case names a disposable fixture a reviewer can access without internal setup. No portal submission, scan, review, or publication is represented by this file.
The source package contains two skills, portable Agent Plugins metadata, OpenAI listing metadata, bundled square icons, and a machine-readable packet that gives exact values and per-hint justifications for all 46 expected MCP tools. Local validators require that packet to match the registered server inventory and annotation values. Both skills require Pascal MCP tools for their tool-backed workflows, so they must not be submitted through the Skills only route.

The With MCP submission remains blocked until an authorized verified OpenAI publisher identity is confirmed, the portal-generated domain challenge is served and accepted, the production endpoint passes Scan Tools, OAuth-compatible reviewer access and credentials exist, and each positive case names a disposable fixture a reviewer can access without internal setup. This is submission preparation only. No portal scan, domain verification, submission, review, approval, publication, or listing is represented by this file.
Loading
Loading