Repository navigation
feat: agent-native discovery and compatibility - #90
Merged
Merged
Conversation
…n name Add the nullable compatibility_json text column to the skills schema and generate its drizzle snapshot and journal entry. Production D1 already has the column and records 0010_greedy_famine.sql in d1_migrations, and wrangler decides pending migrations by file name. The migration keeps that name so production has nothing to apply and fresh databases still create the column. A test pins the name, the SQL and the journal tag, and the deployment guide records the reconciliation.
skillx inspect shows a skill's metadata, requirements and declared compatibility; skillx check gates on a runtime target and answers with its exit code (0 actionable, 4 not, 3 missing, 2 bad input). skillx search takes --compatible and --json and prints the filter report. Lookups reuse use.ts identity parsing, so org/repo/skill resolves through the repo-path endpoint. The contracts package gains the catalog compatibility resolver shared by the web app and the CLI.
Add the read-only agent surfaces: - POST /api/mcp: MCP over JSON-RPC with search_packages, get_package, get_release and check_compatibility. get_release reads through the release access module as the anonymous viewer, so private, draft and quarantined releases are never served. Slug tools resolve aliases. Bodies over 64 KiB get 413, callers are counted on RELEASE_READ_LIMITER under an mcp: key, and handler failures return a generic internal_error while the cause is only logged. - /skills/<slug>.md: the skill as Markdown, always anonymous and publicly cached. React Router has no partial dynamic segment, so the worker entry matches the path before routing; a route-table test pins that. - /llms.txt and /llms-full.txt: a column-limited, bounded query with a probe row instead of a catalog count; ?limit= snaps to fixed sizes and the KV key uses the normalized size. - compatible filter on /api/search, compatibility on the detail API and page, WebMCP read tools, and the harness and publisher docs pages. Runtime ids are validated against one grammar and never echoed, and the pool-exhausted flag now holds for any filled pool.
- llms-full.txt states each entry's scanner risk label, omits the body of a danger listing behind a warning, and fences bodies with a backtick run longer than any inside them so a SKILL.md cannot close its own block. - Persisted compatibility declarations go through the shared contract validator; an invalid shape is treated as no declaration (unknown), never as declared. - /api/mcp runs every tool as the anonymous viewer and no longer reads the request's session or API key. - The .md, llms.txt and llms-full.txt responses send X-Content-Type-Options: nosniff. The llms cache key is bumped for the new document shape.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rebuilds #41 (agent-native discovery and compatibility) on current
main. The slices that main has not already superseded are ported, and every review finding on #41 is fixed and covered by tests.Supersedes #41.
What is ported
skills.compatibility_json(a nullable text column), kept under its production migration name0010_greedy_famine.sql(see "Migration decision" below).skillx-sh):inspectandcheckcommands;search --compatible <runtime[@version]>andsearch --json;/skills/<slug>.mdMarkdown pages;/llms.txtand/llms-full.txt;/api/mcpJSON-RPC endpoint with 4 read-only tools (search_skills,get_package,check_compatibility,get_release);compatiblefilter on search;targetquery param on the skill detail API.docs/api-reference.mdgets a new "Agent Surfaces" section;docs/cli-reference.mddocuments the new commands;docs/deployment-guide.mddocuments the migration reconciliation;CLAUDE.mdgains the new routes.What is dropped
version.tschangeci.ymland typecheck script changesidentifier.tsuse.tsget_kittoolReview findings and fixes
get_releasebypassed release accesstests/releases/mcp-get-release.test.tsfetchSkillBySlug, which resolves aliases and returns the canonical slug.tests/db/mcp-alias-lookup.test.tsget_kitexposuremcp.test.ts,api.mcp.test.ts(4 tools)count.limitis snapped to an allowlist (index 100/250/500/1000, full 25/50/100/200), and the cache key uses the normalized limit.llms-limit.test.ts,public-catalog-index.test.ts,llms-txt.test.tsRetry-After) and then size-capped: bodies over 64 KiB get 413, checked on bothContent-Lengthand streamed bytes.api.mcp.test.tsinternal_error, and the detail is logged server-side only. An unknown tool name or method is not echoed back.mcp.test.ts,api.mcp.test.ts.mdpages read the sessionCache-Control: public, max-age=300with noVary.skill-markdown-handler.test.tscompatibility_jsonisPoolExhaustedhelper.compatibility-filter.test.tscompatiblevalue gets a 400 with no echo, and the empty-result note text is fixed.api.search.test.ts,compatibility-filter.test.ts,mcp.test.tsskills/:slug.mdpartial dynamic segment.mdpaths for GET and HEAD before React Router sees them, and the route is removed from the route table.routes-config.test.ts,skill-markdown-handler.test.tsRate limiter decision: MCP reuses the existing
RELEASE_READ_LIMITERbinding (300 per 60 s) under the key prefixmcp:<caller>. Like the release API, it fails open when the binding is missing, so no new binding or deploy config is needed.Production D1 findings (read-only: SELECT/PRAGMA only)
skills.compatibility_jsonalready exists in production (PRAGMA table_info(skills), cid 28).d1_migrationshas 18 rows, up to0016_personal_kit_proposals.0010_greedy_famine.sql, applied 2026-09-22 from an earlier feat(phase-1): agent-native discovery and compatibility #41 branch.Migration decision
0010_greedy_famine.sql.drizzle-kit generatenow reports "No schema changes".wrangler d1 migrations list skillx-db --remoteon this branch reports "No migrations to apply!".d1_migrations.Deploy findings
wrangler d1 exportis impossible because the database has FTS5 virtual tables.Known gaps
compatibility_jsonyet (no admin or seed flow), so production returns "unknown" for every skill until one exists.error.message. This is pre-existing on main and out of scope here.Test results
pnpm install --frozen-lockfile: ok.pnpm typecheck: exit 0.pnpm test: 91 files, 2021 passed, 2 skipped, 0 failed.pnpm --filter skillx-sh build: ok. Smoke runs passed:--version(0.4.1),--help,inspect --help,check --helpandrelease publish --help.pnpm --filter web build: ok, and the worker bundle includes the Markdown handler.Re-review of bb7776d
An isolated re-review confirmed all four fixes, with tests: danger bodies dropped from llms-full, fences a body cannot close, compatibility declarations validated on every read, MCP pinned to the anonymous viewer, and nosniff plus the v5 cache key. No blockers. Gaps outside these fixes are tracked in #91. Supersedes #41.
🤖 Generated with Claude Code