---
title: "MCP Server"
description: "Connect AI agents to localskills.sh via the Model Context Protocol. Setup for Claude Code, claude.ai, Cursor, Codex CLI, Windsurf, VS Code, and other MCP clients."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.localskills.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server

localskills.sh provides a **Model Context Protocol (MCP)** server that lets AI
agents discover, read, and author your teams' skills without leaving their
workflow. Any MCP client that supports Streamable HTTP with OAuth (Claude Code,
claude.ai, Claude Desktop, Cursor, Codex CLI, Windsurf, VS Code, and others)
can connect directly.

## Server endpoint

The MCP server is available at:

```text
https://localskills.sh/mcp/mcp
```

It uses the **Streamable HTTP** transport (stateless mode). Each request is
independent; no session management is required.

## Authentication

The MCP server requires **OAuth 2.1**. Unauthenticated requests receive a 401
with the standard `WWW-Authenticate` discovery hint, so well-behaved MCP
clients handle the whole flow automatically: they register dynamically, open a
browser authorization page, and receive a Bearer access token scoped to the MCP
resource.

During authorization you pick which of your **organizations** the connection is
for. The token is bound to that organization: the agent sees and writes only
that org's skills. To work with a different organization, re-authenticate and
pick it (most clients have a "reconnect" or "log out" action per server).

The server supports these OAuth scopes:

| Flag | Description |
| --- | --- |
| skills:read | Search skills and fetch their content (always granted) |
| skills:write | Create skills and publish versions (optional; uncheck it on the consent screen for a read-only connection) |
| offline_access | Refresh tokens, so the client stays connected without re-authorizing |
| openid / profile / email | Basic identity for the consent screen |

Discovery metadata lives at the standard well-known URLs:

```text
https://localskills.sh/.well-known/oauth-authorization-server
https://localskills.sh/.well-known/oauth-protected-resource/mcp/mcp
```

> **Note**
>
> API tokens (`lsk_` / `lskt_`) are not accepted by the MCP endpoint, and
> clients that cannot complete a browser OAuth flow cannot connect. For
> headless automation, use the REST API or the CLI instead.

## Client setup

Point any MCP client's Streamable HTTP transport at
`https://localskills.sh/mcp/mcp`. The first connection triggers the browser
OAuth flow. Verified setups for common environments:

### Claude Code

Add the server via the CLI (global):

```sh
claude mcp add localskills --transport http https://localskills.sh/mcp/mcp
```

Or share it with your whole project by committing a `.mcp.json` at the repo
root:

```json
{
  "mcpServers": {
"localskills": {
  "type": "http",
  "url": "https://localskills.sh/mcp/mcp"
}
  }
}
```

Run `/mcp` inside a session to authenticate and inspect the connection.

### claude.ai & Claude Desktop

Go to **Settings → Connectors → Add custom connector** and enter
`https://localskills.sh/mcp/mcp`. Approve the OAuth prompt and the tools become
available in chats (enable them from the search-and-tools menu). On
Team/Enterprise plans an admin may need to add the connector first.

### Cursor

Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):

```json
{
  "mcpServers": {
"localskills": {
  "url": "https://localskills.sh/mcp/mcp"
}
  }
}
```

### Codex CLI

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.localskills]
url = "https://localskills.sh/mcp/mcp"
```

Then run `codex mcp login localskills` to complete the OAuth flow.

### Windsurf

Add to `~/.codeium/windsurf/mcp_config.json` (note the `serverUrl` key):

```json
{
  "mcpServers": {
"localskills": {
  "serverUrl": "https://localskills.sh/mcp/mcp"
}
  }
}
```

### VS Code (Copilot agent mode)

Add to `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
"localskills": {
  "type": "http",
  "url": "https://localskills.sh/mcp/mcp"
}
  }
}
```

### Cline

Add to `~/.cline/data/settings/cline_mcp_settings.json`:

```json
{
  "mcpServers": {
"localskills": {
  "type": "streamableHttp",
  "url": "https://localskills.sh/mcp/mcp"
}
  }
}
```

### opencode

Add to `opencode.json` in your project (or
`~/.config/opencode/opencode.json` globally):

```json
{
  "mcp": {
"localskills": {
  "type": "remote",
  "url": "https://localskills.sh/mcp/mcp",
  "enabled": true
}
  }
}
```

> **Note**
>
> Rolling this out to a whole team? Register the server once as an
> [organization MCP server](/mcp-servers) and members run
> `localskills mcp pull`. The CLI writes the correct config format for every
> tool above.

## Available tools

The server exposes up to nine tools (`create_upload_ticket` registers only when
`UPLOAD_SIGN_SECRET` is configured on the worker). Skill and folder read tools
require the `skills:read` scope; `whoami` requires only an authenticated token,
not a skills scope. Write tools require `skills:write` and are not registered
at all on read-only connections.

### whoami

Returns the identity behind the token: user id, email, granted scopes, and your
teams (each with an id, slug, name, and role). When the token is bound to one
organization, `boundTeamId` identifies it. Takes no arguments. Agents call it
first to learn which `team` values to pass to the tools below.

### search_skills

Search skills and rules visible to you. Returns matching skills with their
slug, publicId, description, and current version; pass one to
`get_skill_content` to load it. Use `scope: "personal"` to search only managed
personal workspaces across your teams. The public catalog is not searchable
over MCP.

| Flag | Description |
| --- | --- |
| query | Search text to match against name, description, and tags |
| type | Filter by "skill" or "rule" |
| tag | Filter by a specific tag |
| team | Limit to one of your teams (tenant slug or id, from whoami) |
| folder | Limit to a folder subtree, e.g. "backend/database"; requires team |
| scope | all (default readable union), team (requires team and excludes managed personal workspaces), mine (skills you created), or personal (your managed personal workspace subtrees) |
| limit | Max results, 1–50 (default 20) |

### get_skill_content

Load a skill's full content. Returns the SKILL.md body plus any additional
package files: text files inlined up to a size budget, binaries listed as
metadata with a signed `downloadUrl` (see
[bundled files](#bundled-files--signed-downloads)). Multi-file packages also
include a signed `packageDownloadUrl` for the whole zip.

| Flag | Description |
| --- | --- |
| slug | The skill slug, or its publicId when the slug is ambiguous (required) |
| version | Version: exact semver (1.2.3), range (^1.0.0), or integer |

> **Note**
>
> Sibling skills in different folders can share a slug. An ambiguous slug
> returns error code `E_AMBIGUOUS` listing the candidates; retry with the
> publicId from search results.

### get_skill_file

Fetch one file from a skill's package by path (for binary files that
`get_skill_content` lists as metadata only). Returns the bytes as base64
(chunked, 32 KiB by default, up to 1 MiB per call) plus a signed `downloadUrl`
that streams the whole file in one HTTP GET. Includes a SHA-256 checksum when
the file is returned whole.

| Flag | Description |
| --- | --- |
| slug | The skill slug, or its publicId (required) |
| path | File path inside the package, as listed by get_skill_content (required) |
| version | Version: exact semver, range, or integer |
| offset | Byte offset for chunked reads (default 0) |
| length | Max bytes to return (default 32 KiB, hard cap 1 MiB) |

### list_folders

Browse a team's folder tree (only paths you can read). Pass a returned path as
the `folder` argument to `search_skills` to scope a search.

| Flag | Description |
| --- | --- |
| team | Tenant slug or id, from whoami or search results (required) |

### get_folder_access

Who can read a folder: every team member with access, resolved live from the
folder ACLs (direct grants, team grants, and admin-bypass roles), plus the
underlying grant list. Open folders report all members as `access: "open"`.
Readable by anyone who can read the folder; editing ACLs stays on the website.

| Flag | Description |
| --- | --- |
| team | Tenant slug or id, from whoami or search results (required) |
| folder | Folder path (e.g. "individual/jane-doe") or folder ID (required) |

### create_skill

Create a new skill. Requires the `skills:write` scope and skill-creation
permission in the target organization. Idempotent on content: retrying an
identical create after a lost response returns the existing skill with
`idempotent: true` instead of creating a duplicate.

| Flag | Description |
| --- | --- |
| name | Skill name, 1–100 characters (required) |
| content | The root SKILL.md body in markdown (required unless uploadId is passed) |
| files | Supporting package files [\{ path, content, encoding? \}]; utf8 by default, base64 for binaries; max 100 files / 10 MB per call |
| uploadId | Commit a package staged via create_upload_ticket (up to 100 MB); mutually exclusive with content/files |
| tenantId | Team ID to create the skill under (required) |
| type | "skill" or "rule" (default: skill) |
| visibility | "public", "private", or "unlisted" (default: private) |
| tags | Up to 10 tags |
| description | Short description |
| folder | Existing folder path (e.g. "backend/database") or folder ID (default: tenant root) |
| onConflict | "suffix" (default) auto-renames the slug when the name is taken; "error" fails with E_CONFLICT naming the existing skill instead of creating a renamed sibling |

### create_upload_ticket

Stage a full-size package (up to 100 MB / 500 files) when the inline `files`
cap is too small. Returns a `fileUrlTemplate` (PUT each raw file, substituting
`{path}` URL-encoded per segment), a `finalizeUrl` (POST once all files are
uploaded; the server assembles the canonical deterministic zip), and the
`uploadId` to pass to `create_skill` / `publish_version`. Requires HTTP egress
and the `skills:write` scope; no parameters. Clients never build zip archives;
packaging is server-side.

### publish_version

Publish a new version of an existing skill. Requires the `skills:write` scope.
Auto-bumps the patch version by default, or you can specify an explicit semver
or bump type. Idempotent on content: publishing bytes identical to the current
version (without a higher explicit semver) returns the current version with
`idempotent: true`.

| Flag | Description |
| --- | --- |
| slug | Skill slug (required) |
| content | The new root SKILL.md body for this version (required unless uploadId is passed) |
| files | Supporting package files [\{ path, content, encoding? \}]; utf8 by default, base64 for binaries; max 100 files / 10 MB per call |
| uploadId | Commit a package staged via create_upload_ticket (up to 100 MB); mutually exclusive with content/files |
| semver | Explicit semver (must be greater than current) |
| bump | Auto-bump: "major", "minor", or "patch" (default: patch) |
| message | Version message / changelog |

## Resources

The server also provides MCP **resources** for browsing skills:

`localskills://skills`: Lists all skills your token can read, with metadata
(slug, name, description, type, semver).

`localskills://skills/{slug}`: Returns the SKILL.md content of one skill.
Agents can browse the list, then read individual skills to inject content into
their context.

`localskills://skills/{slug}/files/{path}`: Reads one file from a skill's
package (current version). Binary files come back as base64 blob contents;
files over 1 MiB must be fetched with `get_skill_file` instead.

## Bundled files & signed downloads

Skills are packages: a root SKILL.md plus optional supporting files (scripts,
templates, binary assets). Agents have two ways to materialize those files:

**Signed download URLs**: `get_skill_content` and `get_skill_file` include a
`downloadUrl` on every non-inlined file (and a `packageDownloadUrl` for the
whole zip). These are HMAC-signed URLs, valid for 1 hour, minted only after the
caller's access checks pass. One `curl -o file '<url>'` streams the file at any
size without passing bytes through model context. It's the best path whenever
the agent's sandbox has HTTP egress.

**Inline base64**: the guaranteed fallback for sandboxes without network access
(MCP calls ride the client's connection, so they always work). Call
`get_skill_file` repeatedly, advancing `offset` by `bytesReturned` until `eof`,
then concatenate and decode the chunks.

## Error handling

Tool failures set `isError` and carry a machine-readable code in
`structuredContent.error.code`:

| Flag | Description |
| --- | --- |
| E_NOT_FOUND | No such skill, team, folder, or file (or no access to it) |
| E_AMBIGUOUS | Slug matches several skills; retry with the publicId |
| E_ACCESS_DENIED | Authenticated but not allowed (missing membership or folder grant) |
| E_INVALID_INPUT | Bad arguments, e.g. folder without team |

## Dynamic skill loading

Agents can discover and load skills on the fly during a conversation. When an
agent encounters a task it doesn't have instructions for, it can search your
team's library, pull in the relevant skill, and continue without interruption.

**Example workflow**: an agent is asked to write a Next.js API route. It
searches for relevant skills, finds one with best practices, and injects it
into context:

```js
// 1. Agent calls search_skills
search_skills({ query: "next.js api routes", type: "skill", limit: 5 })

// 2. Agent picks the best match and fetches its content
get_skill_content({ slug: "nextjs-api-patterns" })

// 3. The skill content is now in the agent's context
//    and it follows the instructions while writing code
```

Because MCP tools are native to the agent's runtime, this happens seamlessly.
The agent decides when to search, what to load, and how to apply the
instructions, with no manual intervention.

> **Note**
>
> Skills loaded via MCP are injected into the agent's current context window.
> They don't persist across sessions unless installed locally via the CLI.

## Loading skills on boot

To load skills from localskills.sh automatically when a session starts, add
instructions to your project's agent configuration file: `AGENTS.md`,
`CLAUDE.md`, `.cursorrules`, or equivalent.

**CLAUDE.md example**: add this to your project's `CLAUDE.md` so Claude Code
loads skills at the start of every session:

```md
# Skills

At the start of each session, use the localskills MCP server to load
the following skills into context:

- `nextjs-api-patterns`: Follow these patterns for all API routes
- `drizzle-d1-guide`: Reference for database queries
- `testing-conventions`: Standards for writing tests

To load a skill, call get_skill_content with the slug above.
If you need additional skills for a task, search localskills.sh first.
```

**AGENTS.md example**: for multi-agent setups, scope skills to specific agent
roles:

```md
# Agent Skills

## All agents
Load these skills from localskills.sh via MCP at session start:
- `code-review-checklist`: Apply during all code reviews
- `security-rules`: Enforce on every change

## Frontend agent
Additionally load:
- `react-component-patterns`: Component architecture guidelines
- `tailwind-conventions`: Styling standards

## Backend agent
Additionally load:
- `api-design-guide`: REST API conventions
- `error-handling-patterns`: Error handling standards
```

**.cursorrules example**: add MCP skill loading to your Cursor project rules:

```text
You have access to the localskills MCP server.
At the start of each conversation, load these skills:

1. Call get_skill_content({ slug: "react-component-patterns" })
2. Call get_skill_content({ slug: "typescript-strict-mode" })

Follow the loaded skill instructions for all code you write.
When asked about an unfamiliar topic, search localskills.sh
for relevant skills before answering.
```

This pattern gives teams shared coding standards and guidelines that every
agent session picks up automatically. Update a skill on localskills.sh and
every agent gets the latest version on its next session (no reinstall needed).

> **Note**
>
> Boot-loaded skills count against the agent's context window. Keep the list
> focused; 3-5 skills is a good starting point. Use `search_skills` for
> on-demand discovery during the session.

## Permissions & access

MCP requests act as the user who authorized the connection, within the
organization chosen during authorization. The server surfaces only your teams'
skills; the public catalog is browsable on the website and REST API, not over
MCP. Folder restrictions apply exactly as they do in the dashboard: skills in
folders you have no grant for don't appear in search, resources, or reads.
Writes (`create_skill`, `publish_version`) additionally require the
corresponding skill permissions in the target organization.

Revoking access works as you'd expect: leave (or be removed from) an
organization and existing tokens bound to it stop returning its skills
immediately.

## Health check

Verify the server is running:

```sh
curl https://localskills.sh/mcp/health
```

Returns `{"status":"ok"}` when healthy.

Source: https://docs.localskills.sh/mcp/index.mdx
