# expose-as-mcp

Turn a Leumas tool, adapter, service or SDK into a distributable, priceable MCP server that any coding agent can call.


# Expose a capability as an MCP server

Leumas turns its tools into MCP servers so any coding agent (Claude, Cursor, a customer's agent) can
call them, and so they can be listed, metered, and **priced** in Leumas Studio. The engine that runs
them is `engines/mcp` (from `mcp-server-library`); the SDK that scaffolds them is `packages/mcp-kit`.

## The contract

An MCP server is a folder under `engines/mcp/mcp-servers/<server-name>/`:

```
engines/mcp/mcp-servers/<server-name>/
├─ server.json          # metadata + tool list (+ pricing)
└─ tools/
   ├─ <toolA>.js        # export default async function({ ...args }) { return result }
   └─ <toolB>.js
```

`server.json`:
```json
{
  "name": "<server-name>",
  "description": "What this server does",
  "enabled": true,
  "tools": [
    { "name": "<toolA>", "description": "...", "inputs": [{ "key": "x", "type": "string", "required": true }], "price": 0 }
  ]
}
```

Each tool file default-exports an async function that takes an args object and returns a result. It
should be a thin wrapper that calls the underlying capability (an `engines/` adapter, a `services/`
endpoint, or a `packages/` SDK) — never re-implement the logic.

## Steps

1. **Identify the capability** to expose (an adapter in `engines/domain`, a service endpoint, an SDK).
2. **Scaffold** with `packages/mcp-kit` (`createServer` / `registerTool`) or by hand from the contract above.
3. **Wire each tool** to call the real capability (import the `@leumas/*` package or fetch the service). Keep tools thin.
4. **Auto-registration** — `engines/mcp` (`loadServers.js`) scans `mcp-servers/` on start and exposes
   `GET /mcp` (list) + `POST /tool` (execute) via `leumas-api`. No manual mounting.
5. **Validate** — smoke-test the handshake, tool list, and a sample `POST /tool` call (schema + result).
6. **Distribute & price** in **Leumas Studio**: enable the server, set per-tool `price`, and choose the
   gate (membership entitlement and/or PassNode usage metering via `packages/auth`).

## Rules

- Tools are **thin adapters** over existing capabilities — no business logic lives in a tool file.
- Every priced tool is gated through `packages/auth` (membership and/or PassNode) — enforced by the API, not the tool.
- Keep servers single-purpose; a "server" groups related tools for one capability.
- Distribution: **hosted** (called on Leumas infra) or **copy-paste**. There is no per-server
  downloadable package — a `@leumas-mcp/<name>` family was described here for a long time and never
  existed, so anyone who followed that sentence went looking for something that was not there.

## How a stranger's agent actually connects

Not by installing a server per capability. One package — **`leumas-mcp`** on npm
(`shared/packages/mcp-bridge`, published unscoped with its `@leumas/*` deps inlined) — is a stdio ⇄
streamable-HTTP bridge pointed at an install:

```sh
npx -y leumas-mcp --url https://api.leumas.tech          # Leumas-hosted
npx -y leumas-mcp --url http://localhost:3000            # the customer's own Studio
npx -y leumas-mcp --url <origin> --site <id>             # one hosted MCP server
```

Everything an install exposes reaches the agent through that one connection, so a tool added here
appears with no release of anything. `pnpm bridge:build` · `pnpm bridge:release` (the gate packs the
tarball, installs it outside the repo and runs a real `initialize` through it).


---
Source: .claude/skills/expose-as-mcp/SKILL.md
Canonical: https://docs.leumas.tech/p/skills/expose-as-mcp
