Serve capabilities through one serializer and add GET /api/v1/executions
The two "refactor later" items from REVIEW-2026-07-30.md; they share the wire types, so they land together. A capability had four divergent wire shapes — the HTTP handler, the MCP adapter, pkg/client, and Maven's vendored copy of it. There is now a single definition in pkg/client, mapped from domain by internal/wire and used by the HTTP list/create/get paths and all four MCP surfaces. It lives in pkg/client rather than internal so external consumers need not vendor internal/domain, and so producer and consumer are literally the same type. The unified shape is a strict superset of all four predecessors; nothing was dropped. It adds enabled and requires_confirmation to the list responses (never omitempty — an absent bool reads as unknown, not false), capability_id to the MCP and client shapes, and the timing/attribute/version fields previously only on get-by-ID. target_types and the list itself now serialize as [] rather than null. Both `id` and `capability_id` are deliberately kept, carrying the same value. Maven decodes `id`; the spec and the rest of the API say `capability_id`. Bearer auth is already a breaking change for that consumer, and stacking a second silent one is the wrong trade — the redundancy stays until every consumer is confirmed on capability_id, then `id` goes in an announced removal. A test pins this and says so. GET /api/v1/executions?entity_id=&since=&limit= implements spec §4.5, which the Command Center needs. `since` reuses the changes-feed cursor convention rather than inventing a second paging idiom. That cursor is the row's implicit SQLite rowid, which is safe only while nothing deletes executions and nothing VACUUMs — both would renumber and silently invalidate outstanding cursors. If retention is ever added, this must become an explicit monotonic column first; the constraint is documented at the query site. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uea55zaiWuEByEDC4UBSdd
This commit is contained in:
+10
-19
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/kami/hexis/internal/execution"
|
||||
"github.com/kami/hexis/internal/nexusclient"
|
||||
"github.com/kami/hexis/internal/storage"
|
||||
"github.com/kami/hexis/internal/wire"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
@@ -145,22 +146,12 @@ func (a *Adapter) handleListCapabilities(ctx context.Context, req mcp.CallToolRe
|
||||
return mcp.NewToolResultError(fmt.Sprintf("list capabilities: %v", err)), nil
|
||||
}
|
||||
|
||||
var result []map[string]any
|
||||
for _, c := range caps {
|
||||
result = append(result, map[string]any{
|
||||
"id": c.ID,
|
||||
"name": c.Name,
|
||||
"description": c.Description,
|
||||
"target_types": c.TargetTypes,
|
||||
"target_entity_id": c.TargetEntityID,
|
||||
"provider": c.Provider,
|
||||
"operation": c.Operation,
|
||||
"risk": c.Risk,
|
||||
"read_only": c.ReadOnly,
|
||||
})
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(result, "", " ")
|
||||
// Same serializer as the HTTP API, so an MCP client and an HTTP client see
|
||||
// an identical capability — including `enabled` and
|
||||
// `requires_confirmation`, which this tool previously hid. Tool
|
||||
// availability is not permission (spec §4.5), but a client that can see
|
||||
// the guard fields can at least explain a 403 instead of guessing.
|
||||
data, _ := json.MarshalIndent(wire.Capabilities(caps), "", " ")
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{Type: "text", Text: string(data)},
|
||||
@@ -179,7 +170,7 @@ func (a *Adapter) handleInspectCapability(ctx context.Context, req mcp.CallToolR
|
||||
return mcp.NewToolResultError(fmt.Sprintf("capability not found: %v", err)), nil
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(cap, "", " ")
|
||||
data, _ := json.MarshalIndent(wire.Capability(cap), "", " ")
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
mcp.TextContent{Type: "text", Text: string(data)},
|
||||
@@ -317,7 +308,7 @@ func (a *Adapter) handleCapabilitiesResource(ctx context.Context, req mcp.ReadRe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, _ := json.MarshalIndent(caps, "", " ")
|
||||
data, _ := json.MarshalIndent(wire.Capabilities(caps), "", " ")
|
||||
return []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: "hexis://capabilities",
|
||||
@@ -339,7 +330,7 @@ func (a *Adapter) handleCapabilityResourceTemplate(ctx context.Context, req mcp.
|
||||
return nil, fmt.Errorf("capability %s: %w", id, err)
|
||||
}
|
||||
|
||||
data, _ := json.MarshalIndent(cap, "", " ")
|
||||
data, _ := json.MarshalIndent(wire.Capability(cap), "", " ")
|
||||
return []mcp.ResourceContents{
|
||||
mcp.TextResourceContents{
|
||||
URI: uri,
|
||||
|
||||
Reference in New Issue
Block a user