Reduce MCP token usage
This commit is contained in:
@@ -74,10 +74,10 @@ type ToolHost interface {
|
||||
// Inspection.
|
||||
ListProcesses(callerID, kindFilter string) []ProcessInfo
|
||||
GetProcessStatus(callerID, processID string) (ProcessStatus, error)
|
||||
GetProjectStatus(callerID string) (ProjectStatus, error)
|
||||
GetProcessOutput(callerID, processID, mode string, sinceOffset int64) (ProcessOutput, error)
|
||||
GetProcessRawOutput(callerID, processID string, sinceOffset int64) (RawOutput, error)
|
||||
SearchOutput(callerID, processID, pattern, kind string, limit int) (SearchResult, error)
|
||||
GetProjectStatus(callerID string, includeTools bool) (ProjectStatus, error)
|
||||
GetProcessOutput(callerID string, args ProcessOutputArgs) (ProcessOutput, error)
|
||||
GetProcessRawOutput(callerID string, args RawOutputArgs) (RawOutput, error)
|
||||
SearchOutput(callerID string, args SearchOutputArgs) (SearchResult, error)
|
||||
WaitForPattern(callerID, processID, pattern string, timeoutSeconds float64, scope string) (matched bool, snippet string, err error)
|
||||
GetProcessPorts(callerID, processID string) ([]PortSighting, error)
|
||||
|
||||
@@ -98,13 +98,13 @@ type ToolHost interface {
|
||||
|
||||
// Scratchpads.
|
||||
ScratchpadList() ([]scratchpad.Entry, error)
|
||||
ScratchpadRead(name string) (content string, revision string, err error)
|
||||
ScratchpadRead(args ScratchpadReadArgs) (ScratchpadReadResult, error)
|
||||
ScratchpadWrite(name, content, expectedRevision string) (revision string, err error)
|
||||
ScratchpadAppend(name, content string) error
|
||||
ScratchpadDelete(name string) error
|
||||
|
||||
// Meta.
|
||||
WhoAmI(callerID string) WhoAmI
|
||||
WhoAmI(callerID string, includeTools bool) WhoAmI
|
||||
Help(callerID, topic string) HelpResponse
|
||||
}
|
||||
|
||||
@@ -157,6 +157,10 @@ type ProjectStatus struct {
|
||||
Scratchpads []scratchpad.Entry `json:"scratchpads"`
|
||||
}
|
||||
|
||||
type ProjectStatusArgs struct {
|
||||
IncludeTools bool `json:"include_tools"`
|
||||
}
|
||||
|
||||
// ProjectMeta is the project root info echoed in many payloads.
|
||||
type ProjectMeta struct {
|
||||
Path string `json:"path"`
|
||||
@@ -166,23 +170,42 @@ type ProjectMeta struct {
|
||||
// ProcessOutput is the get_process_output payload. SPEC §7 enriches
|
||||
// the old read_output result with screen geometry + version.
|
||||
type ProcessOutput struct {
|
||||
Content string `json:"content"`
|
||||
Mode string `json:"mode"`
|
||||
NewOffset int64 `json:"new_offset,omitempty"`
|
||||
ActiveScreen string `json:"active_screen,omitempty"`
|
||||
Rows int `json:"rows,omitempty"`
|
||||
Cols int `json:"cols,omitempty"`
|
||||
Cursor Cursor `json:"cursor"`
|
||||
IdleMS int64 `json:"idle_ms,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
ScreenVersion int64 `json:"screen_version,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Mode string `json:"mode"`
|
||||
NewOffset int64 `json:"new_offset,omitempty"`
|
||||
ActiveScreen string `json:"active_screen,omitempty"`
|
||||
Rows int `json:"rows,omitempty"`
|
||||
Cols int `json:"cols,omitempty"`
|
||||
Cursor Cursor `json:"cursor"`
|
||||
IdleMS int64 `json:"idle_ms,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
ScreenVersion int64 `json:"screen_version,omitempty"`
|
||||
ContentBytes int `json:"content_bytes,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
TruncatedBytes int `json:"truncated_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type ProcessOutputArgs struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Mode string `json:"mode"`
|
||||
SinceOffset int64 `json:"since_offset"`
|
||||
MaxBytes int `json:"max_bytes"`
|
||||
}
|
||||
|
||||
// RawOutput is the get_process_raw_output payload — ANSI preserved.
|
||||
type RawOutput struct {
|
||||
Content string `json:"content"`
|
||||
NewOffset int64 `json:"new_offset"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Content string `json:"content"`
|
||||
NewOffset int64 `json:"new_offset"`
|
||||
Status string `json:"status,omitempty"`
|
||||
ContentBytes int `json:"content_bytes,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
TruncatedBytes int `json:"truncated_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type RawOutputArgs struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
SinceOffset int64 `json:"since_offset"`
|
||||
MaxBytes int `json:"max_bytes"`
|
||||
}
|
||||
|
||||
// SearchResult is search_output's payload.
|
||||
@@ -191,6 +214,14 @@ type SearchResult struct {
|
||||
Truncated bool `json:"truncated"`
|
||||
}
|
||||
|
||||
type SearchOutputArgs struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Pattern string `json:"pattern"`
|
||||
Kind string `json:"kind"`
|
||||
Limit int `json:"limit"`
|
||||
MaxBytes int `json:"max_bytes"`
|
||||
}
|
||||
|
||||
type SearchMatch struct {
|
||||
LineNo int `json:"line_no"`
|
||||
Text string `json:"text"`
|
||||
@@ -245,6 +276,7 @@ type TimerInfo struct {
|
||||
ID string `json:"timer_id"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
BodyTruncated bool `json:"body_truncated,omitempty"`
|
||||
Kind string `json:"kind"` // "delay" | "idle_any" | "idle_all"
|
||||
Status string `json:"status"` // "pending" | "paused"
|
||||
OwnerID string `json:"owner_process_id"`
|
||||
@@ -281,13 +313,14 @@ type SpawnProcessArgs struct {
|
||||
// SendInputArgs is the input shape for send_input — covers text /
|
||||
// paste / key with the optional wait+tail tail-after-send.
|
||||
type SendInputArgs struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Kind string `json:"kind"` // "text" | "paste" | "key"
|
||||
Text string `json:"text"`
|
||||
Key string `json:"key"`
|
||||
Submit *bool `json:"submit"`
|
||||
WaitMS int `json:"wait_ms"`
|
||||
TailMode string `json:"tail_mode"` // "none" | "stream" | "grid"
|
||||
ProcessID string `json:"process_id"`
|
||||
Kind string `json:"kind"` // "text" | "paste" | "key"
|
||||
Text string `json:"text"`
|
||||
Key string `json:"key"`
|
||||
Submit *bool `json:"submit"`
|
||||
WaitMS int `json:"wait_ms"`
|
||||
TailMode string `json:"tail_mode"` // "none" | "stream" | "grid"
|
||||
TailMaxBytes int `json:"tail_max_bytes"`
|
||||
}
|
||||
|
||||
// SendInputResult is the return shape of send_input.
|
||||
@@ -306,6 +339,27 @@ type WhoAmI struct {
|
||||
AvailableTools []string `json:"available_tools"`
|
||||
}
|
||||
|
||||
type WhoAmIArgs struct {
|
||||
IncludeTools bool `json:"include_tools"`
|
||||
}
|
||||
|
||||
type ScratchpadReadArgs struct {
|
||||
Name string `json:"name"`
|
||||
Offset int `json:"offset"`
|
||||
MaxBytes int `json:"max_bytes"`
|
||||
}
|
||||
|
||||
type ScratchpadReadResult struct {
|
||||
Content string `json:"content"`
|
||||
Revision string `json:"revision"`
|
||||
Offset int `json:"offset,omitempty"`
|
||||
NextOffset int `json:"next_offset,omitempty"`
|
||||
ContentBytes int `json:"content_bytes,omitempty"`
|
||||
TotalBytes int `json:"total_bytes,omitempty"`
|
||||
Truncated bool `json:"truncated,omitempty"`
|
||||
TruncatedBytes int `json:"truncated_bytes,omitempty"`
|
||||
}
|
||||
|
||||
// HelpResponse is the help return shape.
|
||||
type HelpResponse struct {
|
||||
Topic string `json:"topic"`
|
||||
@@ -507,61 +561,51 @@ func callTool(h ToolHost, callerID, method string, params json.RawMessage) (any,
|
||||
return st, 0, "", nil
|
||||
|
||||
case "get_project_status":
|
||||
ps, err := h.GetProjectStatus(callerID)
|
||||
var p ProjectStatusArgs
|
||||
_ = unmarshalParamsOptional(params, &p)
|
||||
ps, err := h.GetProjectStatus(callerID, p.IncludeTools)
|
||||
if err != nil {
|
||||
return mapToolError(err)
|
||||
}
|
||||
return ps, 0, "", nil
|
||||
|
||||
case "get_process_output":
|
||||
var p struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Mode string `json:"mode"`
|
||||
SinceOffset int64 `json:"since_offset"`
|
||||
}
|
||||
var p ProcessOutputArgs
|
||||
if err := unmarshalParams(params, &p); err != nil {
|
||||
return nil, codeInvalidParams, err.Error(), nil
|
||||
}
|
||||
if p.Mode == "" {
|
||||
p.Mode = "grid"
|
||||
}
|
||||
out, err := h.GetProcessOutput(callerID, p.ProcessID, p.Mode, p.SinceOffset)
|
||||
out, err := h.GetProcessOutput(callerID, p)
|
||||
if err != nil {
|
||||
return mapToolError(err)
|
||||
}
|
||||
return out, 0, "", nil
|
||||
|
||||
case "get_process_raw_output":
|
||||
var p struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
SinceOffset int64 `json:"since_offset"`
|
||||
}
|
||||
var p RawOutputArgs
|
||||
if err := unmarshalParams(params, &p); err != nil {
|
||||
return nil, codeInvalidParams, err.Error(), nil
|
||||
}
|
||||
out, err := h.GetProcessRawOutput(callerID, p.ProcessID, p.SinceOffset)
|
||||
out, err := h.GetProcessRawOutput(callerID, p)
|
||||
if err != nil {
|
||||
return mapToolError(err)
|
||||
}
|
||||
return out, 0, "", nil
|
||||
|
||||
case "search_output":
|
||||
var p struct {
|
||||
ProcessID string `json:"process_id"`
|
||||
Pattern string `json:"pattern"`
|
||||
Kind string `json:"kind"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
var p SearchOutputArgs
|
||||
if err := unmarshalParams(params, &p); err != nil {
|
||||
return nil, codeInvalidParams, err.Error(), nil
|
||||
}
|
||||
if p.Limit <= 0 {
|
||||
p.Limit = 20
|
||||
p.Limit = 10
|
||||
}
|
||||
if p.Kind == "" {
|
||||
p.Kind = "rendered"
|
||||
}
|
||||
res, err := h.SearchOutput(callerID, p.ProcessID, p.Pattern, p.Kind, p.Limit)
|
||||
res, err := h.SearchOutput(callerID, p)
|
||||
if err != nil {
|
||||
return mapToolError(err)
|
||||
}
|
||||
@@ -731,17 +775,15 @@ func callTool(h ToolHost, callerID, method string, params json.RawMessage) (any,
|
||||
return entries, 0, "", nil
|
||||
|
||||
case "scratchpad_read":
|
||||
var p struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
var p ScratchpadReadArgs
|
||||
if err := unmarshalParams(params, &p); err != nil {
|
||||
return nil, codeInvalidParams, err.Error(), nil
|
||||
}
|
||||
content, rev, err := h.ScratchpadRead(p.Name)
|
||||
res, err := h.ScratchpadRead(p)
|
||||
if err != nil {
|
||||
return nil, codeInternal, err.Error(), nil
|
||||
}
|
||||
return map[string]any{"content": content, "revision": rev}, 0, "", nil
|
||||
return res, 0, "", nil
|
||||
|
||||
case "scratchpad_write":
|
||||
var p struct {
|
||||
@@ -790,7 +832,9 @@ func callTool(h ToolHost, callerID, method string, params json.RawMessage) (any,
|
||||
return map[string]any{"ok": true}, 0, "", nil
|
||||
|
||||
case "whoami":
|
||||
return h.WhoAmI(callerID), 0, "", nil
|
||||
var p WhoAmIArgs
|
||||
_ = unmarshalParamsOptional(params, &p)
|
||||
return h.WhoAmI(callerID, p.IncludeTools), 0, "", nil
|
||||
|
||||
case "help":
|
||||
var p struct {
|
||||
|
||||
Reference in New Issue
Block a user