mcpserver

package
v1.18.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package mcpserver exposes Hermod workflows to Model Context Protocol clients: an MCP client (Claude, ChatGPT, an IDE agent) can list the workflows a user may see, read their status, and run the ones their owner has opted in.

The protocol binding lives in transport/http; this package holds the rules: which workflows are exposed, who may run them, and how a run is delivered and answered.

Index

Constants

View Source
const ExposeTag = "mcp"

ExposeTag is the workflow tag that opts a workflow into MCP. A workflow without it is invisible to MCP clients, whoever asks: exposing an automation to a model is a decision its owner makes, not a default.

It is a tag rather than a new column so that it needs no migration and is edited where tags already are, in the workflow settings.

Variables

View Source
var (
	// ErrNotFound is returned for a workflow that does not exist, is not
	// exposed, or is in a vhost the caller cannot see. The three are
	// deliberately indistinguishable, so the tool is not an oracle for which
	// workflow ids exist.
	ErrNotFound = errors.New("no MCP-exposed workflow with that id")
	// ErrNotAllowed is returned when the caller's role may not run workflows.
	ErrNotAllowed = errors.New("your role may read workflows but not run them; an Editor or Administrator can")
	// ErrNotRunnable is returned for an exposed workflow that has no webhook
	// source, which is the only entry point a run can be delivered through.
	ErrNotRunnable = errors.New("this workflow has no webhook source, so it cannot be run from MCP")
	// ErrNotListening is returned when the workflow's webhook source is not
	// running and could not be woken.
	ErrNotListening = errors.New("the workflow is not running, so nothing received the input; start it and try again")
)
View Source
var ErrModelNotFound = errors.New("no MCP-exposed model by that name")

ErrModelNotFound is returned for a model tool the caller may not use.

Functions

func IsExposed

func IsExposed(wf storage.Workflow) bool

IsExposed reports whether wf carries ExposeTag (any case, surrounding space ignored).

Types

type Caller

type Caller struct {
	// Name labels the caller in the run's request log.
	Name string
	// CanRun is whether the caller's role may start a workflow run (Editor or
	// Administrator). Reading is open to every role.
	CanRun bool
	// MayAccess reports whether the caller may see workflows in vhost.
	MayAccess func(vhost string) bool
}

Caller is who is asking, as the transport authenticated them.

type ModelSource

type ModelSource interface {
	ListModels(ctx context.Context, vhost string) ([]storage.MLModel, error)
	Predict(ctx context.Context, vhost, name string, rows []map[string]any) ([]map[string]any, error)
	// Recommend calls a recommendation model, on the same path as REST, and
	// returns the answer's recommendation_id with it.
	Recommend(ctx context.Context, vhost, name string, req RecommendArgs) ([]recommend.Item, string, error)
	// RecommendScenario is a scenario call ("for", "period", "rules"), on
	// the path of POST .../recommend.
	RecommendScenario(ctx context.Context, vhost, name string, req RecommendArgs) (ScenarioAnswer, error)
}

ModelSource is the part of the model registry the model tools use. Predict is the registry's one inference path, so a model called over MCP is held to the same quotas and metrics as every other caller's.

type ModelTool

type ModelTool struct {
	// Name is predict_<model>, or predict_<model>__<vhost> when the caller
	// can reach two vhosts exposing a model of that name.
	Name        string
	VHost       string
	Model       string
	Description string
	// InputSchema has one property per feature, typed where the type is
	// known, all required; a model that declares no features takes any
	// object as its one row.
	InputSchema map[string]any
	// Recommend marks a recommendation model's tool, recommend_<model>,
	// called with RecommendTool rather than PredictTool.
	Recommend bool
	// contains filtered or unexported fields
}

ModelTool is one model a vhost exposed, as an MCP tool. It is built per request for the caller, like the server it is added to.

type PredictResult

type PredictResult struct {
	Model      string         `json:"model"`
	VHost      string         `json:"vhost"`
	Prediction map[string]any `json:"prediction"`
}

PredictResult is what a model tool answers.

type RecommendArgs

type RecommendArgs struct {
	User        any              `json:"user,omitempty"`
	Item        any              `json:"item,omitempty"`
	For         *ScenarioFor     `json:"for,omitempty"`
	Territory   any              `json:"territory,omitempty"`
	Period      *ScenarioPeriod  `json:"period,omitempty"`
	WithinDays  int              `json:"within_days,omitempty"`
	K           int              `json:"k,omitempty"`
	Rules       []recommend.Rule `json:"rules,omitempty"`
	ExcludeSeen *bool            `json:"exclude_seen,omitempty"`
}

RecommendArgs are a recommend tool's arguments: "user" for a person's items or "item" for items like it, "k" and, for a user, "exclude_seen"; or a scenario call's "for", "territory", "period", "within_days", "k", "rules" and "exclude_seen", as POST .../recommend takes them.

type RecommendResult

type RecommendResult struct {
	Model            string             `json:"model"`
	VHost            string             `json:"vhost"`
	RecommendationID string             `json:"recommendation_id"`
	Items            []recommend.Item   `json:"items,omitempty"`
	Results          []recommend.Result `json:"results,omitempty"`
}

RecommendResult is what a recommend tool answers: items best first for a user or an item, or a scenario call's results; and the recommendation_id feedback on it is sent with.

type RunResult

type RunResult struct {
	// ID is the id of the message the input became.
	ID string `json:"id"`
	// Status is "dispatched" for a workflow that does not reply, "pending"
	// when the wait ran out before the workflow finished, and otherwise what
	// the workflow did: delivered, completed, filtered, dead_lettered or
	// failed.
	Status string `json:"status"`
	Error  string `json:"error,omitempty"`
	// Record is the message as the workflow left it, when it replied.
	Record any `json:"record,omitempty"`
}

RunResult is the answer to run_workflow.

type ScenarioAnswer

type ScenarioAnswer struct {
	RecommendationID string
	Results          []recommend.Result
}

ScenarioAnswer is a scenario call's answer.

type ScenarioFor

type ScenarioFor struct {
	Customer     any   `json:"customer,omitempty"`
	Customers    []any `json:"customers,omitempty"`
	Product      any   `json:"product,omitempty"`
	Salesman     any   `json:"salesman,omitempty"`
	Territory    any   `json:"territory,omitempty"`
	AllCustomers bool  `json:"all_customers,omitempty"`
}

ScenarioFor is who or what a scenario call is for: one of customer, customers, product, salesman or all_customers, and the territory a rule binds to.

type ScenarioPeriod

type ScenarioPeriod struct {
	From string `json:"from"`
	To   string `json:"to,omitempty"`
}

ScenarioPeriod is a visit plan's days, YYYY-MM-DD, both included.

type Service

type Service struct {
	Store Store
	// Wake starts a parked workflow whose source has no listener, and reports
	// whether it started one. Optional.
	Wake func(ctx context.Context, resourceType, path string) bool
	// Models is the model registry the predict_<model> tools call. Optional:
	// without it there are no model tools.
	Models ModelSource
}

Service answers the MCP tools.

func (*Service) List

func (s *Service) List(ctx context.Context, c Caller) ([]WorkflowSummary, error)

List returns the exposed workflows the caller can see.

func (*Service) ModelTools

func (s *Service) ModelTools(ctx context.Context, c Caller, vhosts []string) ([]ModelTool, error)

ModelTools returns a tool for every model exposed to MCP in the vhosts given that the caller may use. A vhost the caller may not use is skipped whatever the list says.

func (*Service) PredictTool

func (s *Service) PredictTool(ctx context.Context, c Caller, t ModelTool, args json.RawMessage) (PredictResult, error)

PredictTool runs one call of a model tool: the arguments, bound to the model's features, are its one row.

func (*Service) RecommendTool

func (s *Service) RecommendTool(ctx context.Context, c Caller, t ModelTool, args json.RawMessage) (RecommendResult, error)

RecommendTool runs one call of a recommend tool.

func (*Service) Run

func (s *Service) Run(ctx context.Context, c Caller, id string, input map[string]any) (RunResult, error)

Run delivers input to an exposed workflow's webhook source, as a POST to that webhook would, and answers the way the source is configured to: with the workflow's result when it replies synchronously (pkg/comm/reply), with "dispatched" otherwise.

The webhook's own API key and signature are not asked for. They authenticate anonymous HTTP callers; an MCP caller has already authenticated as a Hermod user whose role and vhost were checked, against a workflow its owner opted in.

func (*Service) Status

func (s *Service) Status(ctx context.Context, c Caller, id string) (WorkflowStatus, error)

Status returns one exposed workflow's status.

type Store

type Store interface {
	ListWorkflows(ctx context.Context, filter storage.CommonFilter) ([]storage.Workflow, int, error)
	GetWorkflow(ctx context.Context, id string) (storage.Workflow, error)
	GetSource(ctx context.Context, id string) (storage.Source, error)
	CreateWebhookRequest(ctx context.Context, req storage.WebhookRequest) error
}

Store is the part of storage the service reads and writes.

type WorkflowStatus

type WorkflowStatus struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	VHost     string `json:"vhost,omitempty"`
	Active    bool   `json:"active"`
	Status    string `json:"status,omitempty"`
	Processed uint64 `json:"processed"`
	Errors    uint64 `json:"errors"`
	Lag       uint64 `json:"lag"`
}

WorkflowStatus is the answer to get_workflow_status.

type WorkflowSummary

type WorkflowSummary struct {
	ID     string `json:"id"`
	Name   string `json:"name"`
	VHost  string `json:"vhost,omitempty"`
	Active bool   `json:"active"`
	Status string `json:"status,omitempty"`
	// Runnable is whether run_workflow can deliver input to it.
	Runnable bool `json:"runnable"`
	// RepliesWithResult is whether run_workflow waits for and returns what
	// the workflow did, rather than only confirming the input was queued.
	RepliesWithResult bool `json:"replies_with_result"`
}

WorkflowSummary is one entry of list_workflows.

Directories

Path Synopsis
transport
http
Package http mounts the MCP server on Hermod's HTTP API.
Package http mounts the MCP server on Hermod's HTTP API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL