experimental-ext-interceptors/go/sdk/vendor/github.com/segmentio/encoding/json
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Kurt Degiorgio c9900f02ab
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>
2026-04-29 15:29:25 +01:00
..
codec.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
decode.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
encode.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
int.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
json.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
parse.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
README.md Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
reflect.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
reflect_optimize.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
string.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00
token.go Implement Go interceptor chain (#4) 2026-04-29 15:29:25 +01:00

encoding/json GoDoc

Go package offering a replacement implementation of the standard library's encoding/json package, with much better performance.

Usage

The exported API of this package mirrors the standard library's encoding/json package, the only change needed to take advantage of the performance improvements is the import path of the json package, from:

import (
    "encoding/json"
)

to

import (
    "github.com/segmentio/encoding/json"
)

One way to gain higher encoding throughput is to disable HTML escaping. It allows the string encoding to use a much more efficient code path which does not require parsing UTF-8 runes most of the time.

Performance Improvements

The internal implementation uses a fair amount of unsafe operations (untyped code, pointer arithmetic, etc...) to avoid using reflection as much as possible, which is often the reason why serialization code has a large CPU and memory footprint.

The package aims for zero unnecessary dynamic memory allocations and hot code paths that are mostly free from calls into the reflect package.

Compatibility with encoding/json

This package aims to be a drop-in replacement, therefore it is tested to behave exactly like the standard library's package. However, there are still a few missing features that have not been ported yet:

  • Streaming decoder, currently the Decoder implementation offered by the package does not support progressively reading values from a JSON array (unlike the standard library). In our experience this is a very rare use-case, if you need it you're better off sticking to the standard library, or spend a bit of time implementing it in here ;)

Note that none of those features should result in performance degradations if they were implemented in the package, and we welcome contributions!

Trade-offs

As one would expect, we had to make a couple of trade-offs to achieve greater performance than the standard library, but there were also features that we did not want to give away.

Other open-source packages offering a reduced CPU and memory footprint usually do so by designing a different API, or require code generation (therefore adding complexity to the build process). These were not acceptable conditions for us, as we were not willing to trade off developer productivity for better runtime performance. To achieve this, we chose to exactly replicate the standard library interfaces and behavior, which meant the package implementation was the only area that we were able to work with. The internals of this package make heavy use of unsafe pointer arithmetics and other performance optimizations, and therefore are not as approachable as typical Go programs. Basically, we put a bigger burden on maintainers to achieve better runtime cost without sacrificing developer productivity.

For these reasons, we also don't believe that this code should be ported upstream to the standard encoding/json package. The standard library has to remain readable and approachable to maximize stability and maintainability, and make projects like this one possible because a high quality reference implementation already exists.