Metadata-Version: 2.4
Name: elsa-mcp
Version: 0.1.dev10
Summary: MCP server for authoring Elsa Workflows 3 definitions against a live Elsa engine.
Project-URL: Repository, https://github.com/neoblue-tech/elsa-mcp
Project-URL: Issues, https://github.com/neoblue-tech/elsa-mcp/issues
Author-email: Cristina Mudura <info@neobluetech-labs.io>
License: MIT
Keywords: claude,elsa,mcp,model-context-protocol,workflows
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: anyio>=4.4
Requires-Dist: cachetools>=5.4
Requires-Dist: httpx>=0.27
Requires-Dist: mcp[cli]>=1.27.1
Requires-Dist: pydantic-settings>=2.4
Requires-Dist: pydantic>=2.7
Requires-Dist: structlog>=24.4
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# elsa-mcp

An MCP (Model Context Protocol) server in Python that lets an AI client author Elsa Workflows 3
definitions against a **live** Elsa engine. The catalog of activities is fetched from the engine's
own `/elsa/api/descriptors/activities` endpoint — the same one Elsa Studio uses — so the MCP always
reflects whatever modules, custom activities, and dynamic providers are actually loaded on the
target server.

## Architecture

```
[AI client] ──MCP──▶ [elsa-mcp (this project)] ──HTTPS──▶ [Elsa server]
                     (stateless adapter)                   (the engine, untouched)
```

`elsa-mcp` is a **separate process** from Elsa. It depends on Elsa's public REST API and adds:

- LLM-friendly tool surfaces (search → fetch → validate → publish)
- A curated authoring guide as an MCP resource
- Capability gating (`readonly` / `draft` / `full` modes)
- Short-TTL descriptor caching with stale-on-error fallback
- Structured, actionable validation errors

## Install from PyPI

```bash
# Recommended — installs the elsa-mcp command into ~/.local/bin
uv tool install elsa-mcp

# Include preview/dev releases
uv tool install --prerelease=allow elsa-mcp

# Alternative: pipx
pipx install elsa-mcp
```

After install, verify:

```bash
which elsa-mcp        # → /Users/<you>/.local/bin/elsa-mcp
elsa-mcp --help
```

To upgrade or uninstall later:

```bash
uv tool upgrade elsa-mcp
uv tool uninstall elsa-mcp
```

## Configure

Set environment variables (the server reads them at startup):

| Variable | Required | Default | Description |
|---|---|---|---|
| `ELSA_BASE_URL` | yes | `https://localhost:5001` | Base URL of the Elsa server |
| `ELSA_API_KEY` | yes\* | — | API key for `Authorization: ApiKey <KEY>` |
| `ELSA_BEARER_TOKEN` | yes\* | — | Alternative: JWT for `Authorization: Bearer <JWT>` |
| `ELSA_MODE` | no | `readonly` | One of `readonly`, `draft`, `full` |
| `ELSA_VERIFY_TLS` | no | `true` | Set `false` to accept self-signed dev certs |
| `ELSA_CACHE_TTL` | no | `60` | Descriptor cache TTL in seconds |

\* one of `ELSA_API_KEY` / `ELSA_BEARER_TOKEN` is required.

### Capability modes

- `readonly` — only search/get/list/validate tools registered. Safe default for production.
- `draft` — adds create/update tools, but `publish` is forced to `false`. Good for staging.
- `full` — adds publish + dispatch. Use only on dev environments.

Tools are registered **conditionally at startup** — in `readonly` mode the LLM literally cannot
see the write tools.

## Wire up to Claude

### Claude Code

```bash
claude mcp add elsa \
  -e ELSA_BASE_URL=http://localhost:12000 \
  -e ELSA_BEARER_TOKEN=<jwt> \
  -e ELSA_MODE=draft \
  -- "$(which elsa-mcp)"
```

Argument order matters: the server name comes **before** the `-e` flags, and `--` separates them
from the command path. Without `--`, the variadic `-e` swallows the path and you'll get
`error: missing required argument 'commandOrUrl'`.

Add `-s user` to register globally across all projects, or `-s project` to write a shareable
`.mcp.json` in the current repo.

### Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or
`%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "elsa": {
      "command": "/Users/<you>/.local/bin/elsa-mcp",
      "env": {
        "ELSA_BASE_URL": "http://localhost:12000",
        "ELSA_BEARER_TOKEN": "<jwt>",
        "ELSA_MODE": "draft"
      }
    }
  }
}
```

Use the output of `which elsa-mcp` for the `command` path — Claude Desktop does not inherit your
shell `PATH`, so absolute paths are required. Fully quit and relaunch the app after editing.

## Run hosted (Streamable HTTP)

```bash
ELSA_BASE_URL=https://elsa.example.com \
ELSA_API_KEY=... \
ELSA_MODE=draft \
elsa-mcp --http --host 0.0.0.0 --port 8000
```

Put TLS termination + bearer-token validation in a reverse proxy (Caddy / Traefik) in front.

## Spin up a test Elsa server

The repo ships a `docker-compose.yml` with the Elsa server + Studio:

```bash
docker compose up -d
# Elsa server  → http://localhost:12000
# Elsa Studio  → http://localhost:13000  (default login: admin / password)
```

Then point `ELSA_BASE_URL=http://localhost:12000` when running `elsa-mcp`.

## Inspect with the MCP Inspector

```bash
npx @modelcontextprotocol/inspector elsa-mcp
```

Opens a browser UI on `http://localhost:6274` where you can manually exercise tools and resources.

## Develop from source

```bash
git clone https://github.com/neoblue-tech/elsa-mcp.git
cd elsa-mcp
uv sync                              # installs deps + creates .venv
uv run elsa-mcp --help               # run without installing globally
uv tool install --editable .         # OR: install on PATH, live-reloading from source
```

Run tests:

```bash
uv run pytest
```

Unit tests use `respx` to mock the Elsa REST API.

## Releasing

The project is published to PyPI by `.github/workflows/publish.yml`:

- **Push to `main`** → publishes a preview `X.Y.Z.devN` to PyPI (only installable with
  `uv tool install --prerelease=allow elsa-mcp`).
- **Tag `vMAJOR.MINOR.PATCH`** → publishes the stable version + creates a GitHub Release.

Versions are derived from git via `hatch-vcs`; no manual version bumps required.

```bash
git tag v0.1.0
git push origin v0.1.0
# → publishes elsa-mcp==0.1.0
```

## License

MIT
