Skip to content
Open app

Projects

A project is an org-scoped, foldered collection that curates skills from any source — your organization’s first-party skills, public registry skills, and external URLs. Items are grouped into folders by their path, so a project reads like a small tree of related skills.

Projects are the same curation model the registry MCP project tools (my_projects, project_skills, add_skill_to_project) expose to agents: the REST API is how you build and maintain a project, and the MCP tools are how an agent consumes it.

Projects are org-scoped and access is layered on two things: your standing in the org, and (for private projects) your membership on the specific project.

Any org member — viewer and up — can create a project. Whoever creates it becomes its first project admin.

Every project has a visibility of private (the default) or shared.

VisibilityWho can reach it
privateThe creator and explicitly-added project members, plus org owners
sharedGoverned by org roles — any org member can read, editors and up can curate

Project membership is separate from org membership. A project member holds one of three roles:

RoleGrants
viewerRead the project and its items
editorRead plus curate items (add, move, relabel, remove)
adminEverything, plus manage members, change visibility, and delete the project

Each operation requires one of three capabilities. A caller is granted a capability when any of the listed conditions hold:

CapabilityOperationsGranted when
readList / get a project, list membersOrg owner · any project member · project is shared
writeAdd / move / remove items, rename or describeOrg owner · project editor or admin · project is shared and you're an org editor or higher
manageChange visibility, delete, add / update / remove membersOrg owner · project admin
GET /v1/orgs/{orgId}/projects
POST /v1/orgs/{orgId}/projects
GET /v1/orgs/{orgId}/projects/{projectId}
PATCH /v1/orgs/{orgId}/projects/{projectId}
DELETE /v1/orgs/{orgId}/projects/{projectId}

GET /v1/orgs/{orgId}/projects returns each project the caller can read, with its visibility, the caller’s own project role (or null), whether the caller canWrite, and an itemCount:

[
{
"id": "",
"slug": "onboarding",
"name": "Onboarding",
"description": "Skills new engineers should install first",
"visibility": "shared",
"role": "admin",
"canWrite": true,
"createdAt": "2026-01-04T12:00:00.000Z",
"itemCount": 6
}
]

The list is already filtered to what you can reach: org owners see every project in the org; everyone else sees shared projects plus any private project they’re a member of.

Create a project with a name and a URL-safe slug (lowercase, hyphen-separated). The slug must be unique within the org — a collision returns 409. Pass an optional visibility (private by default). The creator becomes the project’s first admin member.

// POST /v1/orgs/{orgId}/projects
{
"name": "Onboarding",
"slug": "onboarding",
"description": "Skills new engineers should install first",
"visibility": "shared"
}
// → 201 { "id": "…", "slug": "onboarding", "name": "Onboarding", "visibility": "shared" }

PATCH accepts name, description, and/or visibility. Changing visibility requires the manage capability (org owner or project admin); a plain rename or re-describe only needs write. DELETE requires manage and cascades to all of the project’s items and members.

GET /v1/orgs/{orgId}/projects/{projectId} returns the project plus its fully resolved items, ordered by folder path then position. It also echoes the caller’s role, and whether they canWrite (curate items) and canManage (members, visibility, delete):

{
"id": "",
"slug": "onboarding",
"name": "Onboarding",
"description": "",
"visibility": "shared",
"role": "admin",
"canWrite": true,
"canManage": true,
"items": [ /* … */ ]
}

role is the caller’s project-membership role, or null when they reach the project purely through org standing (an org owner, or any member of a shared project).

Every item carries these base fields:

FieldTypeDescription
idstringItem id
kind"skill" | "external"Whether the item references a Skillist skill or an external URL
pathstringFolder path within the project (empty string = project root)
positionnumberSort order within the folder
labelstring | nullOptional display label overriding the skill/external name
notestring | nullOptional curator note

Skill items (kind: "skill") additionally resolve:

FieldTypeDescription
skillIdstringReferenced skill id
orgSlugstringOwning org slug (may differ from this org for public skills)
repostringSkill repo name — combine with orgSlug for {org}/{repo}
visibilitystringSkill visibility (e.g. public, private)
descriptionstring | nullRegistry description when published, else the skill's own description

External items (kind: "external") additionally carry:

FieldTypeDescription
externalUrlstringThe external reference URL
externalNamestring | nullOptional display name for the external reference
POST /v1/orgs/{orgId}/projects/{projectId}/items
PATCH /v1/orgs/{orgId}/projects/{projectId}/items/{itemId}
DELETE /v1/orgs/{orgId}/projects/{projectId}/items/{itemId}

Add exactly one kind of item per request. For a skill, provide skillId; for an external reference, provide externalUrl.

// POST …/items — skill
{ "kind": "skill", "skillId": "", "path": "review", "label": "SQL review", "note": "run before every migration" }
// POST …/items — external
{ "kind": "external", "externalUrl": "https://example.com/guide", "externalName": "Style guide", "path": "docs" }

path, label, and note are optional on both kinds; a missing path places the item at the project root.

PATCH …/items/{itemId} moves or relabels an item — send any of path, position, or label (send label: null to clear it). DELETE …/items/{itemId} removes a single item.

Membership controls who can reach a private project and what each person can do. All member operations except the two read endpoints require the manage capability (org owner or project admin).

GET /v1/orgs/{orgId}/members
GET /v1/orgs/{orgId}/projects/{projectId}/members
POST /v1/orgs/{orgId}/projects/{projectId}/members
PATCH /v1/orgs/{orgId}/projects/{projectId}/members/{userId}
DELETE /v1/orgs/{orgId}/projects/{projectId}/members/{userId}

GET /v1/orgs/{orgId}/members lists everyone in the org — the roster you draw from when adding project members. Any org member (viewer and up) can read it. Only these users are eligible to be added to a project.

[
{ "userId": "", "name": "Ada Lovelace", "email": "ada@example.com", "role": "owner" }
]

GET /v1/orgs/{orgId}/projects/{projectId}/members lists the project’s members. It needs read access to the project (org owner, an existing member, or a shared project).

[
{
"userId": "",
"name": "Ada Lovelace",
"email": "ada@example.com",
"role": "admin",
"addedBy": "",
"createdAt": "2026-01-04T12:00:00.000Z"
}
]

Add a member with POST …/members. Provide a userId and an optional role (defaults to viewer). The target must already be an org member — otherwise the call returns 400. Adding someone who is already on the project returns 409.

// POST …/members
{ "userId": "", "role": "editor" }
// → 201 { "ok": true }

PATCH …/members/{userId} changes a member’s role; DELETE …/members/{userId} removes them.

Agents read projects through the registry MCP tools, which enforce the same read/write access model described above:

  • my_projects — list the readable projects across every org the caller belongs to
  • project_skills — resolve a project’s items, grouped by folder path, with install/run commands (needs read access)
  • add_skill_to_project — add a skill or external reference (needs write access)

The MCP tools require an authenticated MCP session — see OAuth authentication.