Implement Go interceptor chain (#4)

## Summary

Implements the Go SDK for MCP interceptors — validation and mutation
middleware for any MCP server.

The framework treats interceptors as first-class MCP primitives: they
are registered on an MCP server, discoverable via `interceptors/list`,
and invocable via `interceptor/invoke` — the same JSON-RPC pattern as
tools, prompts, and resources. A chain engine orchestrates execution
across one or more interceptor servers, and an optional go-sdk
middleware integration applies interceptors automatically on every
matching request/response.

## Architecture

**Three-layer design:**

1. **`interceptors`** — Protocol-agnostic core. Defines interceptor
types (validators, mutators), metadata, invocation, wire types, and
results. Zero MCP dependencies.

2. **`interceptors/mcpserver`** — MCP server integration. Wraps
`mcp.Server` to register `interceptors/list` and `interceptor/invoke` as
custom JSON-RPC methods and advertise interceptor capabilities during
`initialize`.

3. **`interceptors/chain`** — Chain engine. Orchestrates execution
across N interceptor servers with trust-boundary-aware ordering,
priority-based mutation sequencing, parallel validation, audit-mode
isolation, and fail-open/fail-closed semantics.

4. **`interceptors/integrations/gomiddleware`** — Optional go-sdk
middleware. Installs on `mcp.Server` via `AddReceivingMiddleware()` to
automatically run the interceptor chain on every matching
request/response.

**Key design decisions:**

- **Trust-boundary ordering** — request phase: validate → mutate;
response phase: mutate → validate. Validation always guards the boundary
crossing.
- **Wire-level invocation** — the chain calls `interceptor/invoke` over
the MCP transport (even in-process via `LocalChain()`), so local and
remote interceptor servers use the same interface.
- **Audit-mode isolation** — audit-mode mutators compute transformations
without applying them. Only audit mutators pay the serialization cost.
- **Concurrency** — validators run in parallel; mutators run
sequentially in priority order.

### Package Structure

| Package | File | Responsibility |
|---------|------|----------------|
| `interceptors` | `interceptor.go` | Enums, Metadata, Interceptor
interface, Validator/Mutator structs, handler types, Invocation |
| `interceptors` | `wire.go` | Wire types: ListParams/Result,
InvokeParams/Result, InterceptorInfo, event/method constants |
| `interceptors` | `priority.go` | Priority with phase-aware resolution
and JSON marshaling |
| `interceptors` | `doc.go` | Package documentation with examples |
| `interceptors/chain` | `chain.go` | Chain engine: discovery,
filtering, sorting, execution orchestration |
| `interceptors/chain` | `result.go` | ExecutionResult, AbortInfo |
| `interceptors/chain` | `doc.go` | Sub-package documentation |
| `interceptors/mcpserver` | `server.go` | MCP server wrapper,
interceptor registry, capability advertisement, `LocalChain()` |
| `interceptors/mcpserver` | `rpc.go` | `interceptors/list` and
`interceptor/invoke` JSON-RPC handlers |
| `interceptors/mcpserver` | `doc.go` | Sub-package documentation |
| `integrations/gomiddleware` | `middleware.go` | go-sdk `Middleware`
that runs the chain on every request/response |
| `integrations/gomiddleware` | `errors.go` | Abort-to-JSONRPC error
conversion |
| `integrations/gomiddleware` | `doc.go` | Sub-package documentation |

### Documentation

- `doc/DESIGN.md` — architecture, execution model, integration with
go-sdk
- `doc/CONFORMANCE.md` — SEP conformance status
- `doc/PERFORMANCE.md` — per-request cost model

### Examples

- `examples/validator` — blocks dangerous tool calls, demonstrates
`WithContextProvider`
- `examples/mutator` — tags tool responses with audit trail

## Vendor: go-sdk v1.4.0 with custom method support

Vendors `go-sdk` with minimal extensions for custom JSON-RPC methods:

- `Server.AddCustomMethod` — registers a handler that bypasses the typed
pipeline
- `ClientSession.CallCustom` — sends raw JSON-RPC calls for custom
methods

These changes are generic (not interceptor-specific) and enable any MCP
extension to define its own JSON-RPC methods.

## TODO

- More integration tests
- Unit tests
- Support wildcards for event interception (separate PR)

---------

Signed-off-by: Kurt Degiorgio <kdegiorgio@bloomberg.net>
This commit is contained in:
Kurt Degiorgio 2026-04-29 15:29:25 +01:00 • committed by GitHub
commit c9900f02ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 62908 additions and 19 deletions

80
go/sdk/vendor/github.com/segmentio/asm/cpu/arm/arm.go generated vendored Normal file
View file

@ -0,0 +1,80 @@
package arm
import (
"github.com/segmentio/asm/cpu/cpuid"
. "golang.org/x/sys/cpu"
)
type CPU cpuid.CPU
func (cpu CPU) Has(feature Feature) bool {
return cpuid.CPU(cpu).Has(cpuid.Feature(feature))
}
func (cpu *CPU) set(feature Feature, enable bool) {
(*cpuid.CPU)(cpu).Set(cpuid.Feature(feature), enable)
}
type Feature cpuid.Feature
const (
SWP Feature = 1 << iota // SWP instruction support
HALF // Half-word load and store support
THUMB // ARM Thumb instruction set
BIT26 // Address space limited to 26-bits
FASTMUL // 32-bit operand, 64-bit result multiplication support
FPA // Floating point arithmetic support
VFP // Vector floating point support
EDSP // DSP Extensions support
JAVA // Java instruction set
IWMMXT // Intel Wireless MMX technology support
CRUNCH // MaverickCrunch context switching and handling
THUMBEE // Thumb EE instruction set
NEON // NEON instruction set
VFPv3 // Vector floating point version 3 support
VFPv3D16 // Vector floating point version 3 D8-D15
TLS // Thread local storage support
VFPv4 // Vector floating point version 4 support
IDIVA // Integer divide instruction support in ARM mode
IDIVT // Integer divide instruction support in Thumb mode
VFPD32 // Vector floating point version 3 D15-D31
LPAE // Large Physical Address Extensions
EVTSTRM // Event stream support
AES // AES hardware implementation
PMULL // Polynomial multiplication instruction set
SHA1 // SHA1 hardware implementation
SHA2 // SHA2 hardware implementation
CRC32 // CRC32 hardware implementation
)
func ABI() CPU {
cpu := CPU(0)
cpu.set(SWP, ARM.HasSWP)
cpu.set(HALF, ARM.HasHALF)
cpu.set(THUMB, ARM.HasTHUMB)
cpu.set(BIT26, ARM.Has26BIT)
cpu.set(FASTMUL, ARM.HasFASTMUL)
cpu.set(FPA, ARM.HasFPA)
cpu.set(VFP, ARM.HasVFP)
cpu.set(EDSP, ARM.HasEDSP)
cpu.set(JAVA, ARM.HasJAVA)
cpu.set(IWMMXT, ARM.HasIWMMXT)
cpu.set(CRUNCH, ARM.HasCRUNCH)
cpu.set(THUMBEE, ARM.HasTHUMBEE)
cpu.set(NEON, ARM.HasNEON)
cpu.set(VFPv3, ARM.HasVFPv3)
cpu.set(VFPv3D16, ARM.HasVFPv3D16)
cpu.set(TLS, ARM.HasTLS)
cpu.set(VFPv4, ARM.HasVFPv4)
cpu.set(IDIVA, ARM.HasIDIVA)
cpu.set(IDIVT, ARM.HasIDIVT)
cpu.set(VFPD32, ARM.HasVFPD32)
cpu.set(LPAE, ARM.HasLPAE)
cpu.set(EVTSTRM, ARM.HasEVTSTRM)
cpu.set(AES, ARM.HasAES)
cpu.set(PMULL, ARM.HasPMULL)
cpu.set(SHA1, ARM.HasSHA1)
cpu.set(SHA2, ARM.HasSHA2)
cpu.set(CRC32, ARM.HasCRC32)
return cpu
}

View file

@ -0,0 +1,74 @@
package arm64
import (
"github.com/segmentio/asm/cpu/cpuid"
. "golang.org/x/sys/cpu"
)
type CPU cpuid.CPU
func (cpu CPU) Has(feature Feature) bool {
return cpuid.CPU(cpu).Has(cpuid.Feature(feature))
}
func (cpu *CPU) set(feature Feature, enable bool) {
(*cpuid.CPU)(cpu).Set(cpuid.Feature(feature), enable)
}
type Feature cpuid.Feature
const (
FP Feature = 1 << iota // Floating-point instruction set (always available)
ASIMD // Advanced SIMD (always available)
EVTSTRM // Event stream support
AES // AES hardware implementation
PMULL // Polynomial multiplication instruction set
SHA1 // SHA1 hardware implementation
SHA2 // SHA2 hardware implementation
CRC32 // CRC32 hardware implementation
ATOMICS // Atomic memory operation instruction set
FPHP // Half precision floating-point instruction set
ASIMDHP // Advanced SIMD half precision instruction set
CPUID // CPUID identification scheme registers
ASIMDRDM // Rounding double multiply add/subtract instruction set
JSCVT // Javascript conversion from floating-point to integer
FCMA // Floating-point multiplication and addition of complex numbers
LRCPC // Release Consistent processor consistent support
DCPOP // Persistent memory support
SHA3 // SHA3 hardware implementation
SM3 // SM3 hardware implementation
SM4 // SM4 hardware implementation
ASIMDDP // Advanced SIMD double precision instruction set
SHA512 // SHA512 hardware implementation
SVE // Scalable Vector Extensions
ASIMDFHM // Advanced SIMD multiplication FP16 to FP32
)
func ABI() CPU {
cpu := CPU(0)
cpu.set(FP, ARM64.HasFP)
cpu.set(ASIMD, ARM64.HasASIMD)
cpu.set(EVTSTRM, ARM64.HasEVTSTRM)
cpu.set(AES, ARM64.HasAES)
cpu.set(PMULL, ARM64.HasPMULL)
cpu.set(SHA1, ARM64.HasSHA1)
cpu.set(SHA2, ARM64.HasSHA2)
cpu.set(CRC32, ARM64.HasCRC32)
cpu.set(ATOMICS, ARM64.HasATOMICS)
cpu.set(FPHP, ARM64.HasFPHP)
cpu.set(ASIMDHP, ARM64.HasASIMDHP)
cpu.set(CPUID, ARM64.HasCPUID)
cpu.set(ASIMDRDM, ARM64.HasASIMDRDM)
cpu.set(JSCVT, ARM64.HasJSCVT)
cpu.set(FCMA, ARM64.HasFCMA)
cpu.set(LRCPC, ARM64.HasLRCPC)
cpu.set(DCPOP, ARM64.HasDCPOP)
cpu.set(SHA3, ARM64.HasSHA3)
cpu.set(SM3, ARM64.HasSM3)
cpu.set(SM4, ARM64.HasSM4)
cpu.set(ASIMDDP, ARM64.HasASIMDDP)
cpu.set(SHA512, ARM64.HasSHA512)
cpu.set(SVE, ARM64.HasSVE)
cpu.set(ASIMDFHM, ARM64.HasASIMDFHM)
return cpu
}

22
go/sdk/vendor/github.com/segmentio/asm/cpu/cpu.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
// Pakage cpu provides APIs to detect CPU features available at runtime.
package cpu
import (
"github.com/segmentio/asm/cpu/arm"
"github.com/segmentio/asm/cpu/arm64"
"github.com/segmentio/asm/cpu/x86"
)
var (
// X86 is the bitset representing the set of the x86 instruction sets are
// supported by the CPU.
X86 = x86.ABI()
// ARM is the bitset representing which parts of the arm instruction sets
// are supported by the CPU.
ARM = arm.ABI()
// ARM64 is the bitset representing which parts of the arm64 instruction
// sets are supported by the CPU.
ARM64 = arm64.ABI()
)

View file

@ -0,0 +1,32 @@
// Package cpuid provides generic types used to represent CPU features supported
// by the architecture.
package cpuid
// CPU is a bitset of feature flags representing the capabilities of various CPU
// architeectures that this package provides optimized assembly routines for.
//
// The intent is to provide a stable ABI between the Go code that generate the
// assembly, and the program that uses the library functions.
type CPU uint64
// Feature represents a single CPU feature.
type Feature uint64
const (
// None is a Feature value that has no CPU features enabled.
None Feature = 0
// All is a Feature value that has all CPU features enabled.
All Feature = 0xFFFFFFFFFFFFFFFF
)
func (cpu CPU) Has(feature Feature) bool {
return (Feature(cpu) & feature) == feature
}
func (cpu *CPU) Set(feature Feature, enabled bool) {
if enabled {
*cpu |= CPU(feature)
} else {
*cpu &= ^CPU(feature)
}
}

76
go/sdk/vendor/github.com/segmentio/asm/cpu/x86/x86.go generated vendored Normal file
View file

@ -0,0 +1,76 @@
package x86
import (
"github.com/segmentio/asm/cpu/cpuid"
. "golang.org/x/sys/cpu"
)
type CPU cpuid.CPU
func (cpu CPU) Has(feature Feature) bool {
return cpuid.CPU(cpu).Has(cpuid.Feature(feature))
}
func (cpu *CPU) set(feature Feature, enable bool) {
(*cpuid.CPU)(cpu).Set(cpuid.Feature(feature), enable)
}
type Feature cpuid.Feature
const (
SSE Feature = 1 << iota // SSE functions
SSE2 // P4 SSE functions
SSE3 // Prescott SSE3 functions
SSE41 // Penryn SSE4.1 functions
SSE42 // Nehalem SSE4.2 functions
SSE4A // AMD Barcelona microarchitecture SSE4a instructions
SSSE3 // Conroe SSSE3 functions
AVX // AVX functions
AVX2 // AVX2 functions
AVX512BF16 // AVX-512 BFLOAT16 Instructions
AVX512BITALG // AVX-512 Bit Algorithms
AVX512BW // AVX-512 Byte and Word Instructions
AVX512CD // AVX-512 Conflict Detection Instructions
AVX512DQ // AVX-512 Doubleword and Quadword Instructions
AVX512ER // AVX-512 Exponential and Reciprocal Instructions
AVX512F // AVX-512 Foundation
AVX512IFMA // AVX-512 Integer Fused Multiply-Add Instructions
AVX512PF // AVX-512 Prefetch Instructions
AVX512VBMI // AVX-512 Vector Bit Manipulation Instructions
AVX512VBMI2 // AVX-512 Vector Bit Manipulation Instructions, Version 2
AVX512VL // AVX-512 Vector Length Extensions
AVX512VNNI // AVX-512 Vector Neural Network Instructions
AVX512VP2INTERSECT // AVX-512 Intersect for D/Q
AVX512VPOPCNTDQ // AVX-512 Vector Population Count Doubleword and Quadword
CMOV // Conditional move
)
func ABI() CPU {
cpu := CPU(0)
cpu.set(SSE, true) // TODO: golang.org/x/sys/cpu assumes all CPUs have SEE?
cpu.set(SSE2, X86.HasSSE2)
cpu.set(SSE3, X86.HasSSE3)
cpu.set(SSE41, X86.HasSSE41)
cpu.set(SSE42, X86.HasSSE42)
cpu.set(SSE4A, false) // TODO: add upstream support in golang.org/x/sys/cpu?
cpu.set(SSSE3, X86.HasSSSE3)
cpu.set(AVX, X86.HasAVX)
cpu.set(AVX2, X86.HasAVX2)
cpu.set(AVX512BF16, X86.HasAVX512BF16)
cpu.set(AVX512BITALG, X86.HasAVX512BITALG)
cpu.set(AVX512BW, X86.HasAVX512BW)
cpu.set(AVX512CD, X86.HasAVX512CD)
cpu.set(AVX512DQ, X86.HasAVX512DQ)
cpu.set(AVX512ER, X86.HasAVX512ER)
cpu.set(AVX512F, X86.HasAVX512F)
cpu.set(AVX512IFMA, X86.HasAVX512IFMA)
cpu.set(AVX512PF, X86.HasAVX512PF)
cpu.set(AVX512VBMI, X86.HasAVX512VBMI)
cpu.set(AVX512VBMI2, X86.HasAVX512VBMI2)
cpu.set(AVX512VL, X86.HasAVX512VL)
cpu.set(AVX512VNNI, X86.HasAVX512VNNI)
cpu.set(AVX512VP2INTERSECT, false) // TODO: add upstream support in golang.org/x/sys/cpu?
cpu.set(AVX512VPOPCNTDQ, X86.HasAVX512VPOPCNTDQ)
cpu.set(CMOV, true) // TODO: golang.org/x/sys/cpu assumes all CPUs have CMOV?
return cpu
}