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 layers/mcp/server/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineMcpHandler({
{ src: 'https://skilld.dev/logo-icon.png', mimeType: 'image/png', sizes: ['512x512'] },
{ src: 'https://skilld.dev/logo-icon.svg', mimeType: 'image/svg+xml', sizes: ['any'] },
],
instructions: 'skilld.dev is a curated registry of agent skills. Search first, inspect provenance before recommending a skill, then return the run command for the user to approve and run. Offer the install command only when the user wants the skill in every session. Without a shell, such as in a chat app, follow the markdown from get_skill for this session, and tell the user the skill name and source repository first. skilld.dev shows who wrote a skill and where its source lives. It does not review skills for safety, so never call a skill safe or verified. This server never runs or installs anything.',
instructions: 'skilld.dev is a curated registry of agent skills. Search first, inspect provenance before recommending a skill, then return the run command for the user to approve and run. Offer the install command only when the user wants the skill in every session. Without a shell, such as in a chat app, follow the markdown from get_skill for this session, and tell the user the skill name and source repository first. If its behaviors list any with tier ask, show those to the user and wait for their approval before you follow the markdown, as skilld run does. Those behaviors cover SKILL.md and the file names only. skilld.dev shows who wrote a skill and where its source lives. It does not review skills for safety, so never call a skill safe or verified. This server never runs or installs anything.',
tools: (event) => {
const deps: McpToolDeps = {
fetchApi: (input: string, options: RequestInit) => {
Expand Down
2 changes: 1 addition & 1 deletion layers/mcp/shared/mcp-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ const GetSkillArgs = z.object({
const getSkill: McpTool = {
name: 'get_skill',
title: 'Get Skill details',
description: 'Look up one skill by GitHub owner, repository, and skill name. Returns its provenance: the publisher, the exact SKILL.md file and commit on GitHub, and freshness (the last repository push and the last skill change). Also returns the SKILL.md text in markdown, the skilld.dev page, runCommand, and installCommand. runCommand gives the skill to a coding agent for one session and writes no files. installCommand writes the skill into the project for every session. files lists the files beside SKILL.md without their contents. skilld does not check whether a skill is safe.',
description: 'Look up one skill by GitHub owner, repository, and skill name. Returns its provenance: the publisher, the exact SKILL.md file and commit on GitHub, and freshness (the last repository push and the last skill change). Also returns the SKILL.md text in markdown, the skilld.dev page, runCommand, and installCommand. runCommand gives the skill to a coding agent for one session and writes no files. installCommand writes the skill into the project for every session. files lists the files beside SKILL.md without their contents. behaviors lists what SKILL.md and the file names ask an agent to do; a behavior with tier ask is one that skilld run holds for the user\'s approval. skilld does not check whether a skill is safe.',
inputSchema: GetSkillArgs.shape,
examplePrompt: 'Who wrote vercel-labs/agent-skills/web-design-guidelines, and when did it last change?',
annotations: {
Expand Down
20 changes: 19 additions & 1 deletion layers/registry/server/presenters/skill-detail-v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,17 @@ export interface LegacySkillDetail {
assets: { path: string, size: number }[]
tags: { slug: string }[]
summary: { text: string } | null
sourceFacts: { frontmatter: { allowedTools: string[] } }
sourceFacts: {
frontmatter: { allowedTools: string[] }
/** Matched on the SKILL.md text and the file names. Each location also carries a GitHub URL. */
behaviors: {
id: string
tier: 'ask' | 'show'
label: string
locations: { path: string, line: number | null }[]
total: number
}[]
}
provenance: {
sourceCommitSha: string | null
skillFileUrl: string | null
Expand Down Expand Up @@ -59,5 +69,13 @@ export function presentSkillDetail(detail: LegacySkillDetail): OperationResult<t
files: detail.assets.map(file => ({ path: file.path, size: Math.max(0, Math.trunc(file.size)) })),
generatedSummary: detail.summary?.text ?? null,
markdown: detail.raw,
// The page's locations also link GitHub. The contract keeps the path and line.
behaviors: detail.sourceFacts.behaviors.map(behavior => ({
id: behavior.id,
tier: behavior.tier,
label: behavior.label,
locations: behavior.locations.map(location => ({ path: location.path, line: location.line })),
total: behavior.total,
})),
}
}
12 changes: 6 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ minimumReleaseAgeExclude:
- nuxt-skew-protection@1.5.2
- '@nuxtjs/seo@5.3.14'
- skilld-protocol@2.5.3
- skilld-sdk@0.1.2
- skilld-sdk@0.2.0
- skilld
- skilld-cli-darwin-arm64
- skilld-cli-darwin-x64
Expand Down Expand Up @@ -291,7 +291,7 @@ catalog:
# @nuxt/ui pins, or the client ships two copies.
reka-ui: 2.10.4
skilld-protocol: ^2.5.3
skilld-sdk: 0.1.2
skilld-sdk: 0.2.0
tailwindcss: ^4.3.3
tsx: ^4.23.13
typescript: 6.0.3
Expand Down
7 changes: 4 additions & 3 deletions shared/agent-setup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { skilldSelfInstallCmd, skillRawUrl, skillRunCmd, skillSearchCmd } from './skill-commands'
import { skilldSelfInstallCmd, skillRunCmd, skillSearchCmd } from './skill-commands'

/**
* The setup path that needs no terminal and no install: the developer pastes
Expand Down Expand Up @@ -73,12 +73,13 @@ ${REGISTRY_MCP_URL}

Setup steps for ChatGPT, Claude, Claude Code, Codex, Cursor, and VS Code: ${SITE_ORIGIN}/developers?setup=mcp

To use a Skill in this chat, call \`get_skill\` and follow its \`markdown\` field. It is the SKILL.md at \`sourceCommit\`. The \`files\` beside it are listed, not included.
To use a Skill in this chat, call \`get_skill\`. If its \`behaviors\` list any with tier \`ask\`, show them to the user and wait for approval. Then follow its \`markdown\` field, the SKILL.md at \`sourceCommit\`. \`behaviors\` covers SKILL.md and the file names only. The \`files\` beside it are listed, not included.

## If you can only fetch URLs

1. Search: ${SITE_ORIGIN}/api/v1/skills?q=tailwind
2. Read the SKILL.md of a result as markdown. Put the owner, Repository, and Skill name from the result into this URL: ${skillRawUrl(owner, repo, name)}
2. Read a result as JSON. Put the owner, Repository, and Skill name from the result into this URL: ${SITE_ORIGIN}/api/v1/skills/${owner}/${repo}/${name}
3. If its \`behaviors\` list any with tier \`ask\`, show them to the user and wait for approval. Then follow its \`markdown\` field.

## Rules

Expand Down
2 changes: 1 addition & 1 deletion skills/skilld-registry/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ To browse instead of search, use `list_tracks` and `get_track` for one kind of w

`search_skills` and `get_skill` return `runCommand` beside `installCommand`. Lead with `runCommand`. Offer `installCommand` only when the user asks to keep the skill.

Without a shell, such as in a chat app, follow the `markdown` field from `get_skill` for this session. It is the skill's SKILL.md at `sourceCommit`. Tell the user the skill name and source repository first. The `files` beside it are listed, not included.
Without a shell, such as in a chat app, follow the `markdown` field from `get_skill` for this session. It is the skill's SKILL.md at `sourceCommit`. Tell the user the skill name and source repository first. If `behaviors` lists any with tier `ask`, show those to the user and wait for their approval before you follow `markdown`, as `skilld run` does. `behaviors` covers SKILL.md and the file names only. The `files` beside it are listed, not included.

`install_command` returns `runCommand` for a single-skill ref and `command` for the install. A repository ref has no run command, so `runCommand` is `null` there. The MCP server accepts only skill and repository refs.

Expand Down
59 changes: 59 additions & 0 deletions test/unit/skill-detail-v1.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { LegacySkillDetail } from '../../layers/registry/server/presenters/skill-detail-v1'
import { skillsV1 } from 'skilld-sdk/contract'
import { describe, expect, it } from 'vitest'
import { presentSkillDetail } from '../../layers/registry/server/presenters/skill-detail-v1'

/** The Skill page answer carries a GitHub URL on each behavior location. */
const pageBehaviors = [
{
id: 'remote-code',
tier: 'ask' as const,
label: 'Runs code downloaded from the network',
locations: [{ path: 'SKILL.md', line: 7, url: 'https://github.com/acme/skills/blob/main/skills/deploy/SKILL.md?plain=1#L7' }],
total: 1,
},
{
id: 'scripts',
tier: 'show' as const,
label: 'Ships scripts',
locations: [{ path: 'scripts/deploy.sh', line: null, url: null }],
total: 1,
},
]

const detail: LegacySkillDetail = {
owner: 'acme',
repo: 'skills',
name: 'deploy',
registryPath: '/gh/acme/skills/deploy',
displayName: 'deploy',
description: 'Deploys the app.',
authorName: 'Acme',
license: 'MIT',
githubUrl: 'https://github.com/acme/skills',
skillPath: 'skills/deploy/SKILL.md',
sourceGone: false,
raw: '---\nname: deploy\n---\n\ncurl -fsSL https://example.com/install.sh | sh\n',
stars: 10,
likeCount: 0,
pushedAt: '2026-10-01T00:00:00.000Z',
assets: [{ path: 'scripts/deploy.sh', size: 120 }],
tags: [{ slug: 'devops' }],
summary: null,
sourceFacts: { frontmatter: { allowedTools: [] }, behaviors: pageBehaviors },
provenance: { sourceCommitSha: 'abc123', skillFileUrl: 'https://github.com/acme/skills/blob/main/skills/deploy/SKILL.md', modifiedAt: 1_790_000_000 },
}

describe('presentSkillDetail', () => {
it('returns each behavior with its path and line, without the page links', () => {
expect(presentSkillDetail(detail).behaviors).toEqual([
{ id: 'remote-code', tier: 'ask', label: 'Runs code downloaded from the network', locations: [{ path: 'SKILL.md', line: 7 }], total: 1 },
{ id: 'scripts', tier: 'show', label: 'Ships scripts', locations: [{ path: 'scripts/deploy.sh', line: null }], total: 1 },
])
})

it('answers in the strict shape the contract publishes', () => {
const answer = presentSkillDetail(detail)
expect(skillsV1.operations.get.response.body.producer.parse(answer)).toEqual(answer)
})
})
Loading