Files
Maven/internal/ipc/unimplemented.go
T
kami 8d5e357b57 Expose discovered MCP tools through the act allowlist (#251)
Second half of the MCP client: the tools the manager discovers become rows in
the existing act allowlist instead of a parallel capability system.

An MCP tool is encoded in the columns that already exist — cmd
["mcp",<server>,<tool>], scope mcp:<server> — so no migration, and
ProposeTool/EnableTool/DisableTool, tool.Matcher and the confirm turn need no
changes. One branch in Executor.Exec routes such a row to the manager instead
of exec, and "mcp" is never run as a binary.

Discovery only ever PROPOSES. destructive comes from the inverse of the MCP
readOnlyHint, so a tool that does not promise to be read-only inherits the
confirm turn, and enabling stays on /tools behind step-up.

Voice args are positional and MCP args are named, so CallPositional binds only
what it can defend: no required properties runs bare, and a read-only tool with
exactly one required string or number gets the tail. Everything else refuses
with ErrNeedsArgs rather than guessing. The read-only condition was learned
against the live Vikunja server: update_task requires only task_id and takes
the rest as optional, so one guessed argument blanked the fields it did not
mention. A partially-filled write destroys what it omits, so a mutating tool
never receives a guessed argument.

Also: a read-only mcp_servers IPC method and an "MCP servers" card on /tools
showing transport, target and state, with the trust level of a local target
spelled out. There is deliberately no call-a-tool IPC method and no run button,
so mutation keeps exactly one path.

Vikunja #251
2026-08-01 04:36:40 +04:00

134 lines
5.7 KiB
Go

package ipc
import (
"context"
"errors"
"time"
)
// ErrNotImplemented is returned by every UnimplementedCoreAPI method. It is
// deliberately distinct from ErrUnknownMethod (a wire-level "no such
// method exists" verdict) and from any daemon-level "locked" error: this one
// means "this method exists on CoreAPI, but the fake/adapter embedding
// UnimplementedCoreAPI never got a real implementation for it." A test that
// exercises an undeclared method fails loudly on this text instead of
// silently nil-panicking or being mistaken for a legitimate failure.
var ErrNotImplemented = errors.New("ipc: not implemented (unimplemented CoreAPI stub)")
// UnimplementedCoreAPI is the gRPC Unimplemented*Server pattern applied to
// CoreAPI: embed it in a test double or adapter and override only the
// methods you actually exercise. Every method returns ErrNotImplemented, so
// a call that reaches an undeclared method fails loudly and specifically,
// rather than compiling to a silent no-op or nil-pointer panic. This
// replaces the old pattern of hand-writing all 30 no-op stubs per double —
// those were compiler-satisfying padding, not tests of anything.
type UnimplementedCoreAPI struct{}
var _ CoreAPI = UnimplementedCoreAPI{}
func (UnimplementedCoreAPI) WriteFact(ctx context.Context, req WriteFactReq) (int64, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) LatestFact(ctx context.Context, key string) (Fact, error) {
return Fact{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) LatestFactBySource(ctx context.Context, key, source string) (Fact, error) {
return Fact{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) Since(ctx context.Context, key string, now time.Time) (time.Duration, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) Presence(ctx context.Context) (Presence, error) {
return Presence{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) CreateReminder(ctx context.Context, fire time.Time, payload, cron string) (int64, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) MarkReminder(ctx context.Context, id int64, status string) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) ListReminders(ctx context.Context, n int) ([]Reminder, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) RecordNudge(ctx context.Context, rule, channel, message string, ts time.Time) (int64, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) ResolveNudge(ctx context.Context, id int64, outcome string, ts time.Time) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) RecentOutcomes(ctx context.Context, rule string, n int) ([]string, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) RecentFacts(ctx context.Context, n int) ([]Fact, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) CalendarEvents(ctx context.Context, from, to time.Time) ([]Fact, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) RecentNudges(ctx context.Context, n int) ([]Nudge, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) WriteNote(ctx context.Context, ts time.Time, text string, embedding []float32, source string) (int64, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) QueryNotes(ctx context.Context, embedding []float32, k int) ([]Note, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) RecentNotes(ctx context.Context, n int) ([]Note, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) ProposeTool(ctx context.Context, name, utterance, scope string, ts time.Time) (bool, error) {
return false, ErrNotImplemented
}
func (UnimplementedCoreAPI) EnableTool(ctx context.Context, name string, cmd []string, destructive bool, scope string, ts time.Time) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) DisableTool(ctx context.Context, name string) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) DeleteTool(ctx context.Context, name string) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) CaptureTask(ctx context.Context, req CaptureTaskReq) (CaptureTaskResp, error) {
return CaptureTaskResp{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) ListTasks(ctx context.Context, status string) ([]Task, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) SetTaskStatus(ctx context.Context, id int64, status string, ts time.Time) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) ListProposedRoutines(ctx context.Context) ([]ProposedRoutine, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) DismissProposedRoutine(ctx context.Context, id int64) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) AcceptProposedRoutine(ctx context.Context, id int64) error {
return ErrNotImplemented
}
func (UnimplementedCoreAPI) LookupTool(ctx context.Context, name string) (Tool, error) {
return Tool{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) ListTools(ctx context.Context, status string) ([]Tool, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) RevertFact(ctx context.Context, key string) (int64, error) {
return 0, ErrNotImplemented
}
func (UnimplementedCoreAPI) TickTrace(ctx context.Context) (TickTrace, error) {
return TickTrace{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) MorningStatus(ctx context.Context) ([]MorningRoutineStatus, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) MCPServers(ctx context.Context) ([]MCPServerStatus, error) {
return nil, ErrNotImplemented
}
func (UnimplementedCoreAPI) DayPlan(ctx context.Context) (DayPlan, error) {
return DayPlan{}, ErrNotImplemented
}
func (UnimplementedCoreAPI) Chat(ctx context.Context, text string) (string, error) {
return "", ErrNotImplemented
}