Skip to main content

vs-agent

Full-stack agent framework for Viveka Sutra — build agents that serve over HTTP, MCP, or both, from a single codebase.


Overview

vs-agent combines vs-server (HTTP/WebSocket/SSE) and vs-mcp-agent (MCP) into a single VsAgentServer. It lets you write a capability once with @vs_agent_action and declare, per transport, where it can be reached from — an HTTP route, an MCP tool, a queue topic, or any combination — with the same auth, guards, and business logic.

Each transport is opt-in by being present. No http block means no route; no mcp block means no model ever sees it. The way to not expose something is to not mention it, rather than to remember a flag.

The library is fully server-agnostic. VsAgentServer does not hardcode FastAPI or FastMCP — it uses VsServerFactory and VsMcpServerFactory to resolve the correct server implementation at startup. Swapping or adding a new server type requires no changes to application code.


The Problem It Solves

An agent needs to be callable by both humans (via HTTP) and AI clients (via MCP). Without vs-agent, you register the same function twice, apply guards twice, and maintain two sets of route definitions.

Without vs-agent

# HTTP route
@router.post("/v1/docs/search")
async def search_docs_http(query: str, auth=Depends(require_auth)):
    return await _search(query)

# MCP tool — separate registration, separate guard wiring
@mcp.tool(name="search_docs")
async def search_docs_mcp(query: str) -> dict:
    await require_auth()
    return await _search(query)

With vs-agent

@vs_agent_action(
    name="search_docs",
    description="Search VS library documentation",
    http={"url": "/v1/docs/search", "method": "POST"},
    mcp={"name": "search_docs"},
    guards=[VsActionSecurity(roles=["user"])],
)
async def search_docs(query: str) -> VsToolResponse:
    return VsToolResponse(status="success", result=await _search(query))

One function, one guard, available on both protocols — and reachable only over the protocols it names.


Installation

pip install vs-agent

With MCP support:

pip install vs-agent[mcp]

With auth guard support:

pip install vs-agent[mcp,security]

Dependencies

Library Required Purpose
vs-common Yes Config, logging
vs-server Yes HTTP/WebSocket/SSE server
vs-mcp-agent No — install with [mcp] extra MCP server
vs-security No — install with [security] extra JWT auth and VsActionSecurity guard
vs-queue No — not wired yet Queue transport (queue={"listen": ...})

Configuration

VsAgentServer reads HTTP config via vs-server and MCP config via vs-mcp-agent. Both use the same config.ini file — separated by section.

HTTP section (from vs-server):

Key Default Description
server.name vs-agent Server name shown in logs and / response
server.version 0.1.0 Version shown in logs and / response
server.host 0.0.0.0 Host to bind
server.port 8000 HTTP port
server.reload false Enable hot reload (dev only)
server.workers 1 Number of worker processes
server.cors_origins * Comma-separated allowed CORS origins
server.ssl_certfile — Path to TLS certificate file
server.ssl_keyfile — Path to TLS private key file

MCP section (from vs-mcp-agent):

Key Default Description
agent.name vs-agent MCP server name
agent.version 0.1.0 MCP server version
agent.transport streamable-http Transport: stdio, sse, streamable-http
agent.host 0.0.0.0 MCP host
agent.port 8080 MCP port
agent.enable_auth false Enable authentication for HTTP requests and MCP HTTP transports

Authentication is disabled when agent.enable_auth is absent or false. This replaces the former agent.auth_enabled key; migrate existing configuration explicitly. When enabled, pass a VsRequestAuthenticator instance to VsAgentServer. The same instance authenticates HTTP and MCP requests; no auth manager or implicit JWT provider is created. Initialization errors are logged and propagated. Action guards still run when authentication is disabled. MCP stdio calls the same authenticator before message dispatch; credentials must come from an explicit non-HTTP source.

config.ini example:

[server]
name = my-agent
version = 1.0.0
host = 0.0.0.0
port = 8000

[agent]
name = my-agent
version = 1.0.0
transport = streamable-http
host = 0.0.0.0
port = 8080
enable_auth = true

[auth]
secret_key = your-secret-key
algorithm = HS256

[logging]
level = INFO
file_path = ./logs/agent.log

Quick Start

HTTP + MCP (most common)

from vs_common.config.vs_ini_config import VsIniConfig
from vs_common.log.vs_log_manager import VsLogManager
from vs_common.schema.vs_log_config import VsLogConfig
from vs_server.server.vs_fast_api_server import VsFastApiServer  # noqa — auto-registers "fastapi"
from vs_mcp_agent.server.vs_fast_mcp_server import VsFastMcpServer  # noqa — auto-registers "fastmcp"
from vs_agent.server.vs_agent_server import VsAgentServer

import my_agent.actions  # noqa — registers @vs_agent_action functions


def main():
    config = VsIniConfig("config.ini")
    VsLogManager.init(VsLogConfig(level=config.get("logging.level", default="INFO")))

    server = VsAgentServer(config, http="fastapi", mcp="fastmcp")
    server.add_actions()
    server.run()


if __name__ == "__main__":
    main()

HTTP only

server = VsAgentServer(config, http="fastapi")
server.add_actions()
server.run()

MCP only

from vs_mcp_agent.server.vs_fast_mcp_server import VsFastMcpServer  # noqa — auto-registers "fastmcp"

server = VsAgentServer(config, mcp="fastmcp")
server.add_actions()   # required — this is what hands MCP-declaring actions to the tool registry
server.run()

How It All Fits Together

Application Startup
    └── import VsFastApiServer   # auto-registers "fastapi" into VsServerFactory
    └── import VsFastMcpServer   # auto-registers "fastmcp" into VsMcpServerFactory
    └── import my_agent.actions  # @vs_agent_action decorators self-register into VsActionRegistry

VsAgentServer(config, http="fastapi", mcp="fastmcp")
    ├── VsServerFactory.get("fastapi", config)      → VsFastApiServer
    └── VsMcpServerFactory.get("fastmcp", config)   → VsFastMcpServer

server.add_actions()
    ├── VsActionRegistry.http_actions()  → HTTP routes + /capabilities
    ├── VsActionRegistry.mcp_actions()   → VsToolRegistry (VsFastMcpServer wires it at run())
    └── VsActionRegistry.queue_actions() → not wired yet (logs a warning)

server.run()
    ├── MCP server starts on background thread (port 8080)
    └── HTTP server starts on main thread (port 8000)

VsAgentServer

VsAgentServer is the central coordinator. It creates and manages HTTP and MCP server instances via their respective factories.

from vs_agent.server.vs_agent_server import VsAgentServer

server = VsAgentServer(config, http="fastapi", mcp="fastmcp")

Constructor parameters:

Parameter Type Default Description
config VsBaseConfig — Application config
http Optional[str] "fastapi" HTTP server key. Pass None for MCP-only mode.
mcp Optional[str] None MCP server key. Pass "fastmcp" to enable MCP.
authenticator Optional[VsRequestAuthenticator] None Shared authentication instance; required when agent.enable_auth=true.

At least one of http or mcp must be specified — both None raises ValueError.

Methods:

Method Description
add_controller(controller) Register an HTTP @controller class. Delegates to the HTTP server.
add_router(router) Register a raw router. Delegates to the HTTP server.
add_websocket(handler) Register a @websocket handler. Delegates to the HTTP server.
add_sse(handler) Register an @sse handler. Delegates to the HTTP server.
add_actions() Wire every registered action to the transports it declares — HTTP routes plus /capabilities, and MCP tools.
get_app() Return the underlying ASGI app (e.g. FastAPI instance).
run() Start the server(s). MCP runs on a background daemon thread; HTTP runs on the main thread.

mcp property:

server.mcp  # returns the underlying FastMCP instance for advanced configuration

Raises RuntimeError if no MCP server is configured.

All add_* methods return self for chaining:

server = (
    VsAgentServer(config, http="fastapi", mcp="fastmcp")
    .add_controller(HealthController())
    .add_actions()
)
server.run()

@vs_agent_action

Registers a function as one action, and declares per transport where it can be reached from.

from vs_agent.decorator.vs_agent_action_decorator import vs_agent_action
from vs_agent.auth.vs_action_security import VsActionSecurity
from vs_mcp_agent.schema.vs_tool_response import VsToolResponse

@vs_agent_action(
    name="ag.create_new_agent",
    description="Create a new Viveka Sutra agent with the given name and description",
    http={"url": "/v1/agent", "method": "POST"},
    mcp={"name": "create_new_agent"},
    queue={"listen": "agent.create"},
    guards=[VsActionSecurity(roles=["admin"])],
)
async def create_new_agent(request: CreateAgentRequest) -> VsToolResponse:
    ...

Parameters:

Parameter Type Required Default Description
name str Yes — Action identifier. Must be unique; duplicates raise ValueError
description str Yes — Shown in /capabilities and, unless overridden, in the MCP tool list
http dict / HttpTransport No None Present → served over HTTP. {"url": ..., "method": "POST"}
mcp dict / McpTransport No None Present → exposed as an MCP tool. {} accepts all defaults
queue dict / QueueTransport No None Present → consumes a queue topic. {"listen": "topic"}
intents List[Intent] No [] Semantic intents for orchestrator routing
input_schema Dict[str, Any] No None JSON schema for the action input
guards List[Callable] No [] Run before the function on every transport
capability bool No True Whether to advertise the action at all

At least one transport must be declared — an action with none raises ValueError.

Registration also rejects contradictory capability=False plus mcp declarations, unknown fields in any transport, invalid HTTP methods, and HTTP paths without a leading /. HTTP methods must be uppercase: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, or TRACE. The path alias for HTTP url remains supported.

Two actions cannot declare the same HTTP URL and method or resolve to the same MCP tool name. Conflict errors name both actions. These checks happen during decoration/registration, before server wiring. HTTP-only management actions with capability=False remain valid; omit mcp for those actions.

Synchronous and asynchronous actions are supported over HTTP and MCP. Guarded MCP synchronous actions and synchronous guards run in a worker thread with the authenticated context available. Guards must not require HTTP-specific arguments to work on both transports. Queue execution is still not implemented.

The registry copies the supplied guard list, intents, input schema, and transport settings. Changing your original lists or metadata after decoration does not alter the registered action.

Transports are opt-in by presence

Declaration Result
http={"url": "/v1/agent"} only HTTP route. No MCP tool, no queue consumer
mcp={} only MCP tool named after the action. No route at all
queue={"listen": "agent.create"} only Queue consumer. Nothing reachable over HTTP or MCP
all three One function, three ways in

http.method defaults to POST. http also accepts path in place of url, so an @action(path=...) reads the same when migrated. mcp.name defaults to the action name, and mcp.description to the action description — override either when the name a model reads should differ from the internal action id.

If an action declares a transport whose server was not started (mcp={...} with no mcp= passed to VsAgentServer), that is not an error — add_actions() logs a warning and the action is simply unreachable that way.

guards are the action's, not a transport's

One guards list runs before the function no matter which transport the call arrived over. There is deliberately no per-transport guard list: an action that is safe to reach one way and not another is two actions, not one with a clever guard list.

capability is a separate question

A transport says how an action can be reached. capability says whether anybody should be told it exists.

@vs_agent_action(
    name="ag.update_settings",
    description="Update this agent's settings",
    http={"url": "/v1/agent/settings", "method": "PUT"},
    capability=False,
)
async def update_settings(request: SettingsRequest):
    ...

/capabilities and the MCP tool list answer "what can I give you to do". An agent's own management surface — settings, CRUD, anything a person reaches through a screen — still needs its route, but does not belong in that answer: advertising one puts it in front of a planner, which will eventually route a conversation into a CRUD call.

It defaults to True so that forgetting it leaves a capability visible, which is the safe direction to be wrong in.

Import order matters

@vs_agent_action self-registers at import time into VsActionRegistry. Import your action modules before calling server.add_actions().

import my_agent.actions.search   # noqa — triggers registration
import my_agent.actions.summary  # noqa

server.add_actions()
server.run()

@action — deprecated

@action is deprecated. Use @vs_agent_action. It still works and emits a DeprecationWarning.

Its shape bakes in "HTTP always, MCP whenever it is a capability", so an action cannot be HTTP-only without also disappearing from /capabilities, and cannot listen on a queue at all.

# before
@action(name="x", description="…", path="/v1/x", method="POST")

# after
@vs_agent_action(name="x", description="…", http={"url": "/v1/x", "method": "POST"}, mcp={})
@action parameter @vs_agent_action equivalent
path, method http={"url": path, "method": method}
capability=True mcp={} plus capability=True
capability=False omit mcp, pass capability=False
name, description, intents, input_schema, guards unchanged

One behavioural note on migrating: @action registered its MCP tool at decoration time, which meant importing an action module required vs-mcp-agent to be installed. @vs_agent_action leaves that to add_actions(), since the server is the one making the decision.


Shared request authentication

With [agent] enable_auth = true, supply one authenticator instance for HTTP and MCP:

from vs_security import VsBearerTokenAuthenticator, VsJWTProvider, get_auth_context

# A custom authenticator can implement VsRequestAuthenticator.authenticate instead.
# For role guards, pass your VsAuthContext subclass to VsJWTProvider(context_class=...).
authenticator = VsBearerTokenAuthenticator(VsJWTProvider(config))
server = VsAgentServer(
    config, http="fastapi", mcp="fastmcp", authenticator=authenticator,
)
server.add_actions()

# Inside a handler or tool:
# context = get_auth_context()

authenticate(request: VsAuthRequest) -> VsAuthContext receives headers with case-insensitive lookup, a transport (http, mcp for MCP HTTP, or stdio), a path, and an HTTP method. Return validated identity or raise VsAuthenticationError; do not set the context yourself. See vs-security for the interface and bearer-session adapter. Authenticator implementations must support concurrent calls across transport threads and event loops.

The HTTP filter authenticates routes, controllers, actions, /health, and /capabilities; the existing public documentation paths remain excluded. WebSocket authentication continues to use WebSocket guards. MCP HTTP middleware authenticates each request except /.well-known/ metadata paths. Per-message MCP middleware rebinds the validated request identity inside session tasks. Both adapters reset context in finally, including failures and cancellation.

Authentication failures produce 401, insufficient permissions produce 403, and unexpected authentication errors are logged with tracebacks and propagated as server errors. When authentication is disabled, the supplied instance is unused; action guards still run. Peer discovery sends the existing [agent_client] service credentials when requesting health and capabilities.


MCP stdio authentication

Stdio uses the same VsRequestAuthenticator.authenticate() before each MCP request, followed by guards and the handler. Set [agent] transport = stdio and enable_auth = true; pass http=None for an MCP-only agent.

authenticator = VsBearerTokenAuthenticator(
    VsJWTProvider(config), stdio_token_env="VS_AGENT_TOKEN",
)
server = VsAgentServer(
    config, http=None, mcp="fastmcp", authenticator=authenticator,
)

The launcher supplies VS_AGENT_TOKEN in the server process environment. The built-in adapter validates that token through the same session used for HTTP bearer tokens, on every request. Missing or invalid credentials reject the request before the tool executes. A custom authenticator receives VsAuthRequest(transport="stdio", headers={}, method=<MCP method>) and can use its own trusted credential source. Environment credentials identify the launched process, not separate users within it. Stdio authentication errors are MCP protocol errors rather than HTTP status codes.

Configure VS logging to a file with console logging disabled before importing agent/security modules; stdout is reserved for MCP protocol messages. HTTP and MCP HTTP behavior remains as described above.


Authorization with VsActionSecurity

VsActionSecurity is a unified guard that works on both HTTP and MCP. It reads the single shared auth context populated through your VsRequestAuthenticator.

from vs_agent.auth.vs_action_security import VsActionSecurity

@vs_agent_action(
    name="search_docs",
    description="Search documentation",
    http={"url": "/v1/docs/search"},
    mcp={},
    guards=[VsActionSecurity(roles=["user"])],
)
async def search_docs(query: str) -> VsToolResponse:
    ...

No roles (auth only):

guards=[VsActionSecurity()]  # rejects unauthenticated callers, any role allowed

Multiple roles (any match):

guards=[VsActionSecurity(roles=["admin", "editor"])]  # passes if caller has admin OR editor

Multiple guards (all must pass, in order):

guards=[VsActionSecurity(roles=["admin"]), require_verified_account]

How guards run per protocol:

Protocol Auth context source Guard execution
HTTP VsRequestAuthFilter calls the supplied authenticator and sets context Guards run as route dependencies, in order, after the filter sets context
MCP VsMcpAuthMiddleware authenticates; per-message middleware binds context before dispatch Guards called in order before tool function executes
Queue not wired yet —

Lifecycle Hooks

Use lifecycle hooks to run code at server startup and shutdown — creating DB tables, warming caches, closing connections, etc.

HTTP lifecycle (vs-server)

from vs_server.lifecycle.vs_lifecycle import startup, shutdown

@startup
async def warm_cache():
    await VsCacheManager.set("ready", True)

@shutdown
async def flush_cache():
    await VsCacheManager.delete("ready")

Register hook modules with server_registry:

from vs_server.decorator.vs_server_registry import server_registry

@server_registry(hooks="my_agent.lifecycle")
def main():
    ...

MCP lifecycle (vs-mcp-agent)

from vs_mcp_agent.lifecycle.vs_mcp_lifecycle import mcp_startup, mcp_shutdown

@mcp_startup
async def init_mcp_resources():
    await load_tool_index()

@mcp_shutdown
async def cleanup_mcp_resources():
    await close_tool_connections()

Register hook modules with mcp_server_registry:

from vs_mcp_agent.decorator.vs_mcp_server_registry import mcp_server_registry

@mcp_server_registry(hooks="my_agent.mcp_lifecycle")
def main():
    ...

When both are used, HTTP and MCP hooks run independently — HTTP hooks fire when the HTTP server starts/stops, MCP hooks fire when the MCP server starts/stops.


HTTP-only Capabilities

Declare http and omit mcp:

@vs_agent_action(
    name="ag.list_agents",
    description="List all agents",
    http={"url": "/v1/agents", "method": "GET"},
)
async def list_agents():
    ...

For plain routes that are not actions at all, use @controller from vs-server directly and register via server.add_controller():

from vs_server.decorator.vs_controller_decorator import controller, get, post

@controller("/v1/internal")
class InternalController:

    @get("/status")
    async def status(self):
        return {"status": "ok"}

server.add_controller(InternalController())

MCP-only Capabilities

Declare mcp and omit http:

@vs_agent_action(
    name="summarize",
    description="Summarize a document",
    mcp={"name": "summarize_document"},
)
async def summarize(request: SummarizeRequest) -> VsToolResponse:
    ...

For tools that are not actions at all, use @tool from vs-mcp-agent directly:

from vs_mcp_agent.decorator.tool import tool
from vs_mcp_agent.schema.vs_tool_response import VsToolResponse

@tool(name="internal_tool", description="Internal MCP tool only")
async def internal_tool(query: str) -> VsToolResponse:
    ...

These are picked up automatically by VsFastMcpServer at run() — no extra registration needed.


/capabilities Endpoint

server.add_actions() automatically registers a GET /capabilities endpoint on the HTTP server. It returns every action with capability=True — whatever transports it declares — so orchestrators can discover what the agent can do and how to dispatch it.

Actions with capability=False are excluded. They are still served on their routes; they are just not advertised.

Response:

{
  "actions": [
    {
      "name": "ag.create_new_agent",
      "description": "Create a new Viveka Sutra agent",
      "path": "/v1/agent",
      "method": "POST",
      "mcp_tool": "create_new_agent",
      "queue_topic": "agent.create",
      "available_transports": ["http", "mcp"],
      "transports": {
        "http": {
          "available": true,
          "path": "/v1/agent",
          "method": "POST",
          "url": "http://localhost:9011/v1/agent"
        },
        "mcp": {
          "available": true,
          "name": "create_new_agent",
          "description": "Create a new Viveka Sutra agent",
          "transport": "streamable-http",
          "url": "http://localhost:8080/mcp"
        },
        "queue": {"available": false, "topic": "agent.create"}
      },
      "intents": [],
      "input_schema": null
    }
  ]
}

available_transports lists adapters configured and registered for this action on this server. This is registration information, not a live health check. transports describes each declared transport; undeclared transports are null. HTTP paths include server.base_url, and the HTTP URL uses the discovery request's base URL. MCP details include its resolved tool name and description, configured transport, and the server's prepared connection URL. The MCP URL is null before preparation and for stdio. MCP wildcard bind addresses are displayed as localhost, as in the startup log; externally proxied deployments may require a different connection address.

Declaring MCP without configuring an MCP server reports available: false; a queue declaration always reports unavailable until queue execution is implemented. /capabilities is also registered when only MCP/queue actions are declared and an HTTP server is configured.

The legacy path, method, mcp_tool, and queue_topic fields remain for compatibility and describe declarations, regardless of availability. They are null when that transport is undeclared.


Intents

Intent gives each action semantic labels that orchestrators use for routing — matching a user request to the right action without exact string matching.

from vs_agent.schema.vs_action_schema import Intent

@vs_agent_action(
    name="search_docs",
    description="Search VS library documentation",
    http={"url": "/v1/docs/search"},
    intents=[
        Intent(
            name="search",
            description="Find documentation matching a query",
            examples=["how do I configure logging", "what does VsLogManager do"],
        )
    ],
)
async def search_docs(query: str) -> VsToolResponse:
    ...

Intents are returned in /capabilities and are available as metadata on VsActionRegistry entries.


Error Handling

Exceptions from action functions propagate through whichever protocol the call arrived over:

  • On HTTP, vs-server's exception handlers convert them to HTTP responses.
  • On MCP, VsFastMcpServer returns an error result to the MCP client.

Use exceptions from vs-server for standard HTTP error semantics — they are handled automatically:

from vs_server.schema.exceptions import NotFoundException, ServiceUnavailableException

@vs_agent_action(name="get_doc", description="Get a document", http={"url": "/v1/docs/{doc_id}", "method": "GET"})
async def get_doc(doc_id: str) -> VsToolResponse:
    doc = await repo.find(doc_id)
    if doc is None:
        raise NotFoundException(f"Document '{doc_id}' not found")
    return VsToolResponse(status="success", result=doc)

Class Reference


VsAgentServer

Coordinates HTTP and MCP servers. Uses VsServerFactory and VsMcpServerFactory to resolve server implementations. Supports HTTP-only, MCP-only, or HTTP + MCP modes.

Constructor:

Parameter Type Default Description
config VsBaseConfig — Application config
http Optional[str] "fastapi" HTTP server key registered in VsServerFactory. Pass None for MCP-only.
mcp Optional[str] None MCP server key registered in VsMcpServerFactory. Pass "fastmcp" to enable MCP.
authenticator Optional[VsRequestAuthenticator] None Shared authentication instance; required when agent.enable_auth=true.

Methods:

Method Signature Description
add_controller add_controller(controller) -> VsAgentServer Register an HTTP controller. Requires http mode.
add_router add_router(router) -> VsAgentServer Register a raw router. Requires http mode.
add_websocket add_websocket(handler) -> VsAgentServer Register a WebSocket handler. Requires http mode.
add_sse add_sse(handler) -> VsAgentServer Register an SSE handler. Requires http mode.
add_actions add_actions() -> VsAgentServer Wire every registered action to the transports it declares: HTTP routes and /capabilities, and MCP tools via VsToolRegistry.
get_app get_app() -> Any Return the underlying ASGI app. Requires http mode.
run run() -> None Start all servers. MCP on daemon thread, HTTP on main thread.

Property:

Property Type Description
mcp Any The underlying FastMCP instance. Raises RuntimeError if MCP not configured.

Notes:

  • All add_* methods call _require_http() internally and raise RuntimeError if http=None.
  • In HTTP + MCP mode, run() starts MCP on a background daemon thread then blocks on the HTTP server.
  • Import VsFastApiServer before constructing VsAgentServer to ensure "fastapi" is registered.
  • Import VsFastMcpServer before constructing VsAgentServer to ensure "fastmcp" is registered.
  • add_actions() logs a warning for actions declaring a transport whose server is not configured, and for any queue transport — queue is not wired yet.

@vs_agent_action

Decorator. Registers a function in VsActionRegistry with the transports it declares. Self-registers at import time.

Parameters:

Parameter Type Required Default Description
name str Yes — Action key. Must be unique
description str Yes — Shown in /capabilities and the MCP tool list
http Optional[HttpTransport | dict] No None Present → HTTP route
mcp Optional[McpTransport | dict] No None Present → MCP tool
queue Optional[QueueTransport | dict] No None Present → queue consumer (not wired yet)
intents List[Intent] No [] Semantic intents for orchestrator routing
input_schema Dict[str, Any] No None JSON schema for the action input
guards List[Callable] No [] Applied on every transport, in order
capability bool No True Whether the action is advertised

Notes:

  • Each name must be unique across all registrations. Duplicates raise ValueError.
  • An action with no transport raises ValueError.
  • guards must be async callables.
  • Transport dicts are validated into their pydantic models, so a bad key fails at import time rather than at request time.

HttpTransport / McpTransport / QueueTransport

Pydantic models for the transport blocks. Dicts passed to @vs_agent_action are validated into these.

HttpTransport:

Field Type Default Description
url str — Endpoint path. Also accepted as path
method str "POST" HTTP method

McpTransport:

Field Type Default Description
name Optional[str] None → the action name MCP tool name
description Optional[str] None → the action description MCP tool description

QueueTransport:

Field Type Default Description
listen str — Queue topic to consume

@action (deprecated)

Decorator. Kept working for existing agents; emits a DeprecationWarning. Delegates to @vs_agent_action as http={"url": path, "method": method} plus mcp={} when capability is true.

Parameters:

Parameter Type Required Default Description
name str Yes — MCP tool name and action key
description str Yes — Shown in MCP tool list and /capabilities
path str Yes — HTTP endpoint path
method str No "POST" HTTP method
intents List[Intent] No [] Semantic intents for orchestrator routing
input_schema Dict[str, Any] No None JSON schema for the action input
guards List[Callable] No [] Applied on both HTTP and MCP, in order
capability bool No True Whether the action is advertised and exposed over MCP

VsActionSecurity

Guard class for unified HTTP + MCP authorization. Reads from the auth context set by whichever protocol is active.

Constructor:

Parameter Type Default Description
roles Optional[List[str]] [] Required roles. Caller must have at least one. Empty list = any authenticated caller.

Behaviour:

Condition Result
No auth context Raises VsAuthenticationError (401)
Auth context present, no roles required Passes
Auth context present, caller has required role Passes
Auth context present, caller lacks required role Raises VsInsufficientRolesError (403)

Notes:

  • On HTTP, VsRequestAuthFilter calls the supplied authenticator and sets the shared context.
  • On MCP, auth context is set by VsMcpAuthMiddleware (from vs-mcp-agent).
  • VsActionSecurity reads from the same context variable regardless of protocol.

VsActionRegistry

Class-level registry of all registered actions. Thread-safe.

Methods:

Method Signature Description
register_action register_action(name, description, intents, input_schema, guards, fn, capability=True, http=None, mcp=None, queue=None) -> None Register an action. Raises ValueError if the name is taken
register register(name, description, path, method, intents, input_schema, guards, fn, capability=True) -> None Deprecated. The @action shape; delegates to register_action
get_all get_all() -> Dict[str, _ActionEntry] Every action, whatever it is reachable over
capabilities capabilities() -> Dict[str, _ActionEntry] Only actions with capability=True
http_actions http_actions() -> Dict[str, _ActionEntry] Only actions declaring an http transport
mcp_actions mcp_actions() -> Dict[str, _ActionEntry] Only actions declaring an mcp transport
queue_actions queue_actions() -> Dict[str, _ActionEntry] Only actions declaring a queue transport

_ActionEntry fields: name, description, intents, input_schema, guards, fn, capability, http, mcp, queue.

_ActionEntry derived properties: path and method (from http, None without it), mcp_name and mcp_description (from mcp, falling back to the action's own name and description).


Intent

Pydantic model. Semantic label for an action — used by orchestrators for routing.

Fields:

Field Type Required Description
name str Yes Short intent identifier
description str Yes What this intent means
examples Optional[List[str]] No Example user phrases that trigger this intent

ActionCapability

Pydantic model. Metadata for a single action as returned by /capabilities.

Fields:

Field Type Description
name str Action name
description str Action description
path Optional[str] HTTP endpoint path. None without an http transport
method Optional[str] HTTP method. None without an http transport
mcp_tool Optional[str] MCP tool name. None without an mcp transport
queue_topic Optional[str] Queue topic. None without a queue transport
available_transports List[str] Adapters configured and registered for this action
transports ActionTransports HTTP/MCP/queue details, including availability, endpoint information, and MCP description
intents List[Intent] Semantic intents
input_schema Optional[Dict[str, Any]] JSON schema for input

CapabilitiesResponse

Pydantic model. Response from GET /capabilities.

Fields:

Field Type Description
actions List[ActionCapability] All advertised actions — those with capability=True

Metadata

Release files for vs-agent 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vs-agent 0.1.6
File Size Uploaded Provenance
vs_agent-0.1.6.tar.gz 62.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vs-agent 0.1.6
File Interpreter ABI Platform
vs_agent-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 96.8 kB

Release files / vs_agent-0.1.6.tar.gz

Download URL vs_agent-0.1.6.tar.gz
Size 62.6 kB
Tags Source
SHA-256 checksum
How to use checksums
8864bb3b1a70db7ff9d915df97b566aef3aad79ccac3287ba1f0634af988983f
BLAKE2b-256 checksum
How to use checksums
c783c085e12ad86652ae3c77fb760354048273c2e4c633b370e3a2a073b82355
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release files / vs_agent-0.1.6-py3-none-any.whl

Download URL vs_agent-0.1.6-py3-none-any.whl
Size 34.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
390a7ccfc3dad338f6cb1f9910a8a87db709ca1b6f75e0ef1258c628e297ff6b
BLAKE2b-256 checksum
How to use checksums
8e8102ccc0bd424881081b7bfd50cf9f11bc43156d4c2bfe63fa1eadb2242251
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Security for vs-agent 0.1.6

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page