tinyrelayfile
tinyrelay/internal/mcp/mcp.go/

internal/mcp/mcp.go raw
1package mcp
2
3import (
4 "encoding/base64"
5 "encoding/json"
6 "fmt"
7 "strings"
8)
9
10// Version is the protocol revision this package implements.
11const Version = "2026-07-28"
12
13// SupportedVersions lists the protocol revisions the server accepts.
14var SupportedVersions = []string{Version}
15
16// Header names mirrored from the JSON-RPC body so intermediaries can route
17// without parsing it. Comparison of names is case-insensitive; values are
18// case-sensitive.
19const (
20 HeaderProtocolVersion = "MCP-Protocol-Version"
21 HeaderMethod = "Mcp-Method"
22 HeaderName = "Mcp-Name"
23)
24
25// Reserved _meta keys carried on every request and result.
26const (
27 MetaProtocolVersion = "io.modelcontextprotocol/protocolVersion"
28 MetaClientCapabilities = "io.modelcontextprotocol/clientCapabilities"
29 MetaClientInfo = "io.modelcontextprotocol/clientInfo"
30 MetaServerInfo = "io.modelcontextprotocol/serverInfo"
31)
32
33// JSON-RPC error codes. The -32020 to -32099 range is reserved for the MCP
34// specification and only the defined codes may be emitted.
35const (
36 CodeParse = -32700
37 CodeInvalidRequest = -32600
38 CodeMethodNotFound = -32601
39 CodeInvalidParams = -32602
40 CodeInternal = -32603
41 CodeHeaderMismatch = -32020
42 CodeMissingClientCapability = -32021
43 CodeUnsupportedProtocolVersion = -32022
44)
45
46// Error is a JSON-RPC error object. Status is the HTTP status the transport
47// answers with and is never serialized.
48type Error struct {
49 Code int `json:"code"`
50 Message string `json:"message"`
51 Data any `json:"data,omitempty"`
52 Status int `json:"-"`
53}
54
55func (e *Error) Error() string { return fmt.Sprintf("mcp: %d %s", e.Code, e.Message) }
56
57// Request is an incoming JSON-RPC request or notification. ID is nil for a
58// notification.
59type Request struct {
60 JSONRPC string `json:"jsonrpc"`
61 ID json.RawMessage `json:"id,omitempty"`
62 Method string `json:"method"`
63 Params json.RawMessage `json:"params,omitempty"`
64}
65
66// Notification reports whether the message carries no id and therefore
67// expects no response.
68func (r Request) Notification() bool { return len(r.ID) == 0 }
69
70// Response is an outgoing JSON-RPC result or error.
71type Response struct {
72 JSONRPC string `json:"jsonrpc"`
73 ID json.RawMessage `json:"id,omitempty"`
74 Result any `json:"result,omitempty"`
75 Error *Error `json:"error,omitempty"`
76}
77
78// Implementation names a client or server.
79type Implementation struct {
80 Name string `json:"name"`
81 Version string `json:"version,omitempty"`
82}
83
84// params is the subset of request parameters the transport inspects.
85type params struct {
86 Meta map[string]json.RawMessage `json:"_meta"`
87 Name string `json:"name"`
88 URI string `json:"uri"`
89 Cursor string `json:"cursor"`
90 Arguments json.RawMessage `json:"arguments"`
91}
92
93const (
94 sentinelPrefix = "=?base64?"
95 sentinelSuffix = "?="
96)
97
98// EncodeHeaderValue renders a body value for the Mcp-Name or Mcp-Param
99// headers. Values outside the safe ASCII set, with surrounding whitespace, or
100// that already look like the sentinel are Base64 encoded.
101func EncodeHeaderValue(value string) string {
102 if headerSafe(value) && !sentinel(value) {
103 return value
104 }
105 return sentinelPrefix + base64.StdEncoding.EncodeToString([]byte(value)) + sentinelSuffix
106}
107
108// DecodeHeaderValue reverses EncodeHeaderValue. Plain values must consist of
109// visible ASCII, space and horizontal tab.
110func DecodeHeaderValue(value string) (string, error) {
111 if sentinel(value) {
112 raw, err := base64.StdEncoding.DecodeString(strings.TrimSuffix(strings.TrimPrefix(value, sentinelPrefix), sentinelSuffix))
113 if err != nil {
114 return "", fmt.Errorf("invalid Base64 header value")
115 }
116 return string(raw), nil
117 }
118 if !headerSafe(value) {
119 return "", fmt.Errorf("header value contains invalid characters")
120 }
121 return value, nil
122}
123
124func sentinel(value string) bool {
125 return strings.HasPrefix(value, sentinelPrefix) && strings.HasSuffix(value, sentinelSuffix) && len(value) >= len(sentinelPrefix)+len(sentinelSuffix)
126}
127
128func headerSafe(value string) bool {
129 if value != strings.TrimSpace(value) {
130 return false
131 }
132 for i := 0; i < len(value); i++ {
133 c := value[i]
134 if c == ' ' || c == '\t' || (c >= 0x21 && c <= 0x7e) {
135 continue
136 }
137 return false
138 }
139 return true
140}
141
142func errorf(code, status int, format string, args ...any) *Error {
143 return &Error{Code: code, Status: status, Message: fmt.Sprintf(format, args...)}
144}
145
146func supported(version string) bool {
147 for _, v := range SupportedVersions {
148 if v == version {
149 return true
150 }
151 }
152 return false
153}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs