Skip to main content
Glama
README.md
# skill-mcp

Serve company documentation through one read-only MCP server. Point the server
at any compatible Markdown folder and agents get two portable tools:
`search(query)` to discover relevant information and `fetch(id)` to retrieve an
authoritative document with its canonical URL and metadata.

The content model is deliberately broader than Agent Skills. It works for
internal-library guidance, data-source instructions, engineering standards,
runbooks, architecture notes, and any other bounded company context.

## Run the example catalog

Requires Python 3.14 or newer and [uv](https://docs.astral.sh/uv/).

```bash
git clone https://github.com/jacobragsdale/skill-mcp.git
cd skill-mcp
uv sync --locked
SKILL_MCP_CONTENT_ROOT=/absolute/path/to/skill-mcp/examples/context uv run skill-mcp
```

Connect an MCP client to `http://127.0.0.1:8000/mcp`. The readiness endpoint
validates the live catalog and reports its document count:

```bash
curl http://127.0.0.1:8000/health
```

FastAPI's OpenAPI UI is available at `http://127.0.0.1:8000/docs`.

To keep the path in a local file, copy `.env.example`, set its required value,
and load it explicitly:

```bash
cp .env.example .env
uv run --env-file .env skill-mcp
```

The root is selected once at startup. Restart with a different
`SKILL_MCP_CONTENT_ROOT` to serve another compatible folder. Documents inside
the selected folder are validated and reread on every tool call, so edits go
live without rebuilding or restarting the server.

## Add information to search and fetch

Create a UTF-8 Markdown file anywhere below the configured content root. Keep
one file focused on one fetchable topic and begin it with this strict YAML
frontmatter:

```markdown
---
id: data/customer-orders
title: Customer order data source
url: https://docs.example.com/data/customer-orders
summary: Find governed customer order data and choose the supported table.
metadata:
  owner: Data Platform
  authority: example
  updated: "2026-08-04"
---

# Customer order data

The supported source is ...
```

Then validate the entire root:

```bash
uv run skill-mcp-validate /absolute/path/to/company-context
```

Repository contributors can invoke the repo-scoped
[`$add-company-context`](.cursor/skills/add-company-context/SKILL.md) skill for
the complete authoring and retrieval-regression workflow. Do not hardcode
topic-specific branches in `search` or `fetch`; adding a valid document makes
it available to both tools automatically.

## Content contract

The configured root is scanned recursively for non-hidden `*.md` files. Every
discovered file must satisfy the contract; one invalid document fails the
catalog rather than silently serving partial company guidance.

| Field | Requirement |
| --- | --- |
| `id` | Required stable lowercase identifier using path segments and hyphens, such as `engineering/python-settings`. It must be unique across the root. |
| `title` | Required human-readable title, 1–200 characters. |
| `url` | Required absolute HTTP or HTTPS canonical source URL. |
| `summary` | Required search-oriented summary, 1–500 characters. |
| `metadata` | Optional string-to-string provenance fields such as `owner`, `authority`, `version`, and `updated`. Quote YAML values that would otherwise become dates or numbers. |
| body | Required non-empty Markdown. A document may contain at most 60,000 characters so a fetch stays bounded. |

Keep identifiers stable when moving files: callers fetch by frontmatter `id`,
not by filesystem path. Hidden directories, hidden files, and common cache
directories are ignored. Symlinks that escape the configured root, duplicate
identifiers, malformed YAML, unknown frontmatter fields, non-UTF-8 text, and
empty bodies are rejected.

## MCP reference

The server intentionally exposes tools only. That is the common denominator
across coding agents; clients do not need MCP resource or prompt support.

| Tool | Input | Structured result |
| --- | --- | --- |
| `search` | `query: string` (1–500 characters) | `{results: [{id, title, url}]}` with at most 10 BM25-ranked matches. |
| `fetch` | `id: string` from `search` | `{id, title, text, url, metadata}` for one exact document. |

Both tools are declared read-only, non-destructive, idempotent, and
closed-world. Search is deterministic keyword retrieval over identifiers,
titles, summaries, metadata, and Markdown bodies. Titles and summaries receive
extra weight; an exact phrase in either receives a further boost. A query with
no matching terms returns an empty `results` array.

The server instructions tell agents to search whenever company-specific
context could help, fetch before relying on a result, preserve canonical URLs
as provenance, and avoid inventing internal facts when the catalog has no
answer.

## Production boundary

The included process binds only to `127.0.0.1:8000` and has no built-in
authentication or browser CORS policy. For company deployment, put it behind
your existing identity-aware gateway or reverse proxy, terminate TLS there,
and record per-tool latency, result count, fetched document id, caller, and
errors there or in structured application logs. Do not log full queries or
document bodies unless your privacy policy explicitly permits it.

Authentication controls who can reach a server; it is not document-level
authorization. Run separate catalogs or add an authorization-aware storage
adapter before mixing content with different audiences. The server is
read-only and never executes, edits, or installs any served content.

## Develop

```bash
uv sync --locked
uv run pytest
uv run pre-commit run --all-files
uv build --no-sources
```

Retrieval examples live in `examples/context/`, and human-readable evaluation
queries live in `evals/retrieval.json`. The test suite checks every evaluation,
strict content rejection, live root swapping, the official in-memory MCP client
contract, tool annotations and schemas, and the FastAPI lifespan and health
route. CI runs the suite and all repository checks on Python 3.14.