Files
juliusbrussee__caveman/mcp/protocol.go
T
Julius Brussee 82864c8f4c feat: ship Caveman 2.0.0 — engine, proxy, SDKs, unified CLI
Single-commit port of the Caveman 2 tree onto the original repo history
(candidate caveman-2@88878a4 applied over main@3098342). No unrelated-history
merge; original history untouched.

Preserved v1 surfaces:
- skills/caveman/SKILL.md byte-for-byte, plus plugin mirror and caveman.skill zip
- legacy CI (Node 18/20/22 installer + Python 3.11 suites) alongside engine-ci
- cli/install.js compatibility shim forwarding to bin/install.js
- .codex/config.toml ships both hooks and codex_hooks for older Codex (#617)
- README Trendshift + Skills.sh badges and local star-history asset
- v1.10.0-pinned curl installs; benchmarks/tmp-starcharts ignore rules

Fixed for public same-repo release:
- SECURITY.md vulnerability reporting points at this repository
- release-binaries.yml publishes to this repo via job-scoped GITHUB_TOKEN
  (cross-repo PAT removed); binary-release environment still gates signing
- packages/cli/PUBLISHING.md rewritten for packages/ layout and same-repo flow
- profile-contract and provider-catalog lanes skip lane-scope gates on
  initial import so the launch PR can gate on their real validation steps
- ANNOUNCEMENT.md network claim corrected: compression is local; requests
  still go to the provider you configured

Caveman 2 stays opt-in: bin-v1.0.0 binaries and @caveman-ai/cli publish
separately after this lands and soaks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLvb5QniwYjmcawyssQNw9
2026-08-11 16:46:10 +02:00

103 lines
3.3 KiB
Go

package mcp
import (
"bytes"
"encoding/json"
)
// JSON-RPC 2.0 error codes used by the server.
const (
codeParseError = -32700
codeInvalidRequest = -32600
codeMethodNotFound = -32601
codeInvalidParams = -32602
codeInternalError = -32603
)
// rpcRequest is one inbound JSON-RPC request. A request whose ID is absent (or
// explicitly null) is a notification and gets no response — see isNotification.
type rpcRequest struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}
// isNotification reports whether a request carries no usable id and therefore
// must never be answered. Per JSON-RPC 2.0 a notification is a request with no
// "id" member; a literal `"id":null` cannot correlate a response either, so both
// are treated as notifications (the id-less reply was sub-issue #4 of #139).
func isNotification(id json.RawMessage) bool {
if len(id) == 0 {
return true
}
return bytes.Equal(bytes.TrimSpace(id), []byte("null"))
}
type rpcResponse struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id,omitempty"`
Result any `json:"result,omitempty"`
Error *rpcError `json:"error,omitempty"`
}
type rpcError struct {
Code int `json:"code"`
Message string `json:"message"`
}
func successResponse(id json.RawMessage, result any) rpcResponse {
return rpcResponse{JSONRPC: "2.0", ID: id, Result: result}
}
func errorResponse(id json.RawMessage, code int, msg string) rpcResponse {
return rpcResponse{JSONRPC: "2.0", ID: id, Error: &rpcError{Code: code, Message: msg}}
}
// ToolResult is an MCP tools/call result: a content list plus an error flag.
type ToolResult struct {
Content []ToolContent `json:"content"`
IsError bool `json:"isError"`
}
type ToolContent struct {
Type string `json:"type"`
Text string `json:"text"`
}
// ToolText wraps a value as a single JSON text content item (the agent receives
// JSON it can parse).
func ToolText(v any) ToolResult {
b, err := json.Marshal(v)
if err != nil {
return ToolError("cave_marshal_failed", "could not encode result")
}
return ToolResult{Content: []ToolContent{{Type: "text", Text: string(b)}}, IsError: false}
}
// ToolRawText wraps a raw string (e.g. recovered original bytes) as text content.
func ToolRawText(s string) ToolResult {
return ToolResult{Content: []ToolContent{{Type: "text", Text: s}}, IsError: false}
}
// ToolError is a fail-closed tool error: isError=true with a cave_snake_code, so
// a host never mistakes it for a successful payload.
func ToolError(code, msg string) ToolResult {
b, _ := json.Marshal(map[string]string{"error": code, "message": msg})
return ToolResult{Content: []ToolContent{{Type: "text", Text: string(b)}}, IsError: true}
}
// ObjectSchema builds a JSON-Schema object for a tool's inputSchema.
func ObjectSchema(props map[string]any, required ...string) map[string]any {
m := map[string]any{"type": "object", "properties": props}
if len(required) > 0 {
m["required"] = required
}
return m
}
// StringProp is a string property schema with a description.
func StringProp(description string) map[string]any {
return map[string]any{"type": "string", "description": description}
}