Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Selvans

БесплатноНе проверен

A framework that makes any web application natively operable by AI agents through the Model Context Protocol (MCP), replacing browser automation with structured

GitHubEmbed

Описание

A framework that makes any web application natively operable by AI agents through the Model Context Protocol (MCP), replacing browser automation with structured UI semantic trees and typed backend operations.

README

selvans is a framework that makes any web application natively operable by AI agents through the Model Context Protocol (MCP).

Instead of using browser automation, AI agents connect directly to your application through a structured protocol — observing the UI semantic tree and calling typed tools to interact with it.


How It Works

 External AI (Claude Desktop, Cursor…)
          │  MCP / SSE
          ▼
  ┌──────────────────┐
  │  selvans-core │  ← Hub service (WebSocket + MCP server + admin UI)
  └──────┬─────┬─────┘
         │ WS  │ WS
    ┌────┘     └────┐
    ▼               ▼
Angular App    Python Backend
(selvans-   (selvans-
  angular)       python)
  1. selvans-core is the central hub. It exposes an MCP server (SSE) that external AI clients connect to, and a WebSocket endpoint that your frontend and backend connect to.
  2. selvans-angular integrates into your Angular app. It registers a semantic UI tree and built-in tools (click_element, form_input, get_page_state…) with the Core.
  3. selvans-python integrates into your Python/FastAPI backend. It registers typed service operations with the Core — tool schemas and argument validation are derived from your type hints by the official MCP SDK.
  4. The AI calls tools and operations through the Core, which routes them to the right app in real time.

Repository Structure

selvans/
├── packages/
│   ├── core/                     # Hub service + Docker image (Python module: selvans_core)
│   ├── lib/                      # Client libraries
│   │   ├── selvans-angular/      # Angular library (npm)
│   │   ├── selvans-python/       # Python client library (PyPI)
│   │   └── selvans-java/         # Java library (planned)
│   ├── demo/                     # Demo stack
│   │   ├── selvans-angular-demo/ # Angular demo app
│   │   ├── selvans-python-demo/  # FastAPI demo backend
│   │   └── docker-compose.yml    # Demo Docker Compose
│   ├── setup/                    # npm installer for the Claude plugin (WIP)
│   └── claude-plugin/            # Claude plugin: agents/commands/skills (WIP)
└── .github/workflows/            # CI (docker-images.yml, release.yml)

There is no root Nx/pnpm workspace — packages are organized by shippable artifact and each one builds, tests and ships on its own.


Quick Start

The demo stack runs entirely in Docker (only Docker required — no Node/pnpm on the host). The Core is consumed as a published image; until it exists on Docker Hub, build it locally first:

# 1. Build the Core image locally (one-off, until it's published to Docker Hub)
docker build -t gabrieleconsonni/selvans-core:latest packages/core

# 2. Start the stack: Core (:8080) + Python demo (:8001) + Angular dev server (:4200, live-reload)
docker compose -f packages/demo/docker-compose.yml up -d --build

Open http://localhost:4200 — the selvans-panel should show "connected".

Live-reload: the Angular source is bind-mounted into the container. Saving a file in packages/demo/selvans-angular-demo/ or packages/lib/selvans-angular/ triggers an automatic browser update (~1 s latency due to polling on Docker Desktop Windows).

First startup: the Angular container build (in-container npm install + esbuild) takes ~90–180 s.

Prod-like (Angular built and served via nginx instead of the live-reload dev server):

docker compose -f packages/demo/docker-compose.yml --profile full up -d --build

Note: selvans-angular-dev (default profile, live-reload) and selvans-angular-demo (profile full, nginx) both bind port 4200 — never run both at once.

Teardown (stop + wipe volumes):

docker compose -f packages/demo/docker-compose.yml down -v

See [[Getting-started]] in the wiki for the full guide and troubleshooting.


Manual start

1. Start the Core

# Run the Core on its own (local build)
docker compose -f packages/core/docker-compose.yml up --build
# Admin UI → http://localhost:8080/ui
# MCP SSE  → http://localhost:8080/mcp/sse

2. Integrate the Angular Frontend

npm install selvans-angular
// app.module.ts
import { SelvansModule } from 'selvans-angular';

@NgModule({
  imports: [
    SelvansModule.forRoot({
      coreUrl: 'http://localhost:8080',
      appId: 'my-app'
    })
  ]
})
export class AppModule {}

Mark your components with semantic directives:

<main [SelvansNode]="{ id: 'main', template: 'layout', description: 'Main content area' }">
  <form [SelvansNode]="{ id: 'login-form', template: 'form', description: 'User login form' }">
    <input [SelvansTarget]="'email-input'" type="email" />
    <button [SelvansTarget]="'submit-btn'">Login</button>
  </form>
</main>

3. Integrate the Python Backend

pip install selvans
# main.py
from typing import Literal
from selvans import SelvansBeApp, SelvansBeConfig, SelvansService, operation

class TaskService(SelvansService):
    name = "tasks"
    description = "Task management"

    @operation("list", description="List tasks, optionally filtered by status")
    async def list_tasks(self, status: Literal["pending", "completed"] | None = None) -> list[dict]:
        return await db.list_tasks(status=status)

    @operation("create", description="Create a new task")
    async def create_task(self, title: str, priority: Literal["high", "medium", "low"] = "medium") -> dict:
        return await db.create_task(title, priority)

surface = SelvansBeApp(SelvansBeConfig(core_url="http://localhost:8080"))
surface.register(TaskService())
app = surface.create_app()

Type hints drive the AI-facing schema: Literal[...] becomes a JSON Schema enum, X | None an optional field, and out-of-range arguments are rejected before your method runs.

4. Connect an AI Client

Add the Core's MCP endpoint to your AI client (e.g. Claude Desktop claude_desktop_config.json):

{
  "mcpServers": {
    "selvans": {
      "url": "http://localhost:8080/mcp/sse"
    }
  }
}

The AI can now observe your app's UI and call your backend operations directly.

When the Core runs with authentication enabled (Selvans_AUTH_MODE=enforce), the client must present a bearer token ("headers": { "Authorization": "Bearer <token>" }) and only sees the tools it is authorized for — see Authentication.


Run the Full Demo

docker compose -f packages/demo/docker-compose.yml up -d --build
# → Core :8080 + Python demo :8001 + Angular dev container :4200 (live-reload)

Or prod-like, with Angular served via nginx:

docker compose -f packages/demo/docker-compose.yml --profile full up -d --build
# → Core :8080 + Python demo :8001 + Angular nginx :4200 (static bundle)
Service URL Mode
selvans-core http://localhost:8080 both
Admin UI http://localhost:8080/ui both
MCP SSE endpoint http://localhost:8080/mcp/sse both
Python demo backend http://localhost:8001 both
Angular demo frontend http://localhost:4200 dev (live-reload) / prod-like (nginx)

Note: selvans-angular-dev (dev, live-reload) and selvans-angular-demo (profile full, nginx) both bind port 4200 — do not start them together.


Packages

Package Description Docs
selvans-angular Angular library — semantic UI tree + built-in tools docs
selvans-python Python library — FastAPI integration + service operations docs
selvans-core Hub service — MCP server + WebSocket hub + admin UI docs

Built-in Frontend Tools

The Angular library registers these tools automatically:

Tool Description
get_page_state Returns current URL, title, and visible text
navigate Navigate to a route by path
get_elements List all elements marked with [SelvansTarget]
click_element Click an element by its target ID
form_input Read or set the value of a form field by target ID

In-App AI Chat (built-in)

External MCP clients (Claude Desktop, Cursor) run the AI in a separate window. For a chat that lives next to the UI it drives, the Core ships a built-in agent loop — no AI vendor SDK, just plain HTTP to the provider you configure.

        ┌──────────────── selvans-core ────────────────┐
Browser │  WS /chat/ws →  ChatOrchestrator (agent loop) │ ──HTTP──▶ LLM
 chat   │                      │ router.dispatch()      │   (Anthropic /
panel / │                      ▼                        │    OpenAI / Ollama)
console │              connected apps (FE + BE) via WS   │
        └───────────────────────────────────────────────┘

The orchestrator asks the model for tool calls, runs them through the same ToolRouter that MCP clients use, and feeds results back until the model answers.

The browser talks to it over a stateful, streaming WebSocket (/chat/ws): the Core owns the conversation, so what the user types reaches the model and the loop streams back live — the assistant's narration, each tool call and its result, then the reply. The chat is a real-time mirror of the agent, and because the Core (not an external MCP client) drives the loop, the browser → model → browser round-trip is just a normal bidirectional socket. A stateless POST /chat (whole transcript in, final reply out) remains for simple callers.

Two surfaces are exposed over it:

  • Frontend panel — drop <selvans-chat /> into your Angular app. The chat is scoped to that app's tools, so the assistant operates the UI the user is looking at, live.
  • Multi-app consolehttp://localhost:8080/ui/console. One chat, an app-switcher across every registered app, and the selected app's UI embedded in an iframe beside it.

Configure the provider via env (see .env.example) — the Core reaches it over HTTP, so switching is only a matter of these values:

AI_PROVIDER=ollama          # ollama | openai | anthropic
OLLAMA_MODEL=qwen2.5:7b-instruct
# OPENAI_API_KEY=…   OPENAI_MODEL=gpt-4o-mini
# ANTHROPIC_API_KEY=…   ANTHROPIC_MODEL=claude-sonnet-4-20250514

With ollama (the default) no API key is needed — use a model that supports tool calling. If the selected provider is missing its key, the chat returns a clear, actionable error (an error frame over the socket, or an error body from POST /chat) and the rest of the Core keeps working.


Authentication

App-to-Core authentication is available as an opt-in first step (phase 0-1 of the auth design). Apps authenticate to the Core with a shared-secret token bound to their appId, and the Core refuses to let one app hijack another's live appId.

Configured via env on the Core:

# off (default) | permissive (verify + log, don't block) | enforce (reject)
Selvans_AUTH_MODE=enforce
# per-app shared secrets, keyed by appId
Selvans_APP_TOKENS='{"demo-backend":"change-me"}'

Each app then presents its token — SelvansBeConfig(token=…) (Python), SelvansModule.forRoot({ token: … }) (Angular). With AUTH_MODE=off behaviour is unchanged, so existing deployments are unaffected. Roll out with permissive to observe violations in the logs before switching to enforce.

The same AUTH_MODE also governs AI/MCP clients: each client is given a bearer token and an allow-list of apps, so it can only see and call the tools it is authorized for (tool calls are attributed to the real client id, not a shared constant):

Selvans_MCP_CLIENTS='{"claude-desktop":{"token":"change-me","allow":["demo-backend"]}}'

In enforce mode you must configure both Selvans_APP_TOKENS and Selvans_MCP_CLIENTS, otherwise apps / AI clients are rejected.

End-user identity

The AI can act on behalf of an end user: it presents the user's token in the X-Selvans-User header, the Core verifies it (HS256 JWT when Selvans_USER_JWT_SECRET is set; otherwise an opaque, unverified subject id), and propagates the identity to your app on every call. Every tool call is then attributable to a real user in the telemetry — closing the "we don't know which user made a request" gap.

Selvans_USER_JWT_SECRET=change-me   # verify the user JWT (omit for opaque dev mode)

Your backend reads the caller without changing operation signatures:

from selvans import operation, current_user_id

@operation("list", description="List the current user's tasks")
async def list_tasks(self) -> list[dict]:
    return await db.list_tasks(owner=current_user_id())   # None if no user propagated

Angular receives the same context on the toolCall$ stream. Identity is propagated and audited regardless of AUTH_MODE; it is about attribution, not access control, so an absent/invalid user token never blocks a call (it is logged).


License

Distributed under the Apache 2.0 license — see LICENSE and NOTICE.

from github.com/gabrieleakeron/selvans

Установка Selvans

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/gabrieleakeron/selvans

FAQ

Selvans MCP бесплатный?

Да, Selvans MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Selvans?

Нет, Selvans работает без API-ключей и переменных окружения.

Selvans — hosted или self-hosted?

Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.

Как установить Selvans в Claude Desktop, Claude Code или Cursor?

Открой Selvans на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Selvans with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории design