Design: Meshery MCP Server architecture and registration interface

Meshery MCP Server: Architecture and Registration Design

Status: draft for community review. This aligns with the scaffold already up in PR #28 (mark3labs/mcp-go, stdio transport, environment config, CI). Feedback on any section is welcome, especially section 3.

1. Problem and goals

AI assistants cannot work with Meshery today. An engineer using Claude, Cursor, or Copilot has no way to list designs, inspect a cluster, resolve a component schema, or deploy a pattern, so they end up copying YAML back and forth by hand. The Meshery MCP Server closes that gap by exposing Meshery through the Model Context Protocol.

The server gives an agent three ways to work with Meshery:

  • Observe through read-only resources backed by MeshSync-discovered topology.
  • Act through tools covering the design lifecycle, cluster connections, the model and component registry, environments and workspaces, and performance testing.
  • Be steered through prompt templates for guided workflows like design review and application deployment.

2. Scope and non-goals

In scope: stdio and streamable HTTP transports, the tool/resource/prompt surface, a shared Meshery client as the single integration boundary, authentication with multi-instance contexts, tests, CI, and a released binary and container image.

Non-goals: this is the MCP Server, separate from the Meshery AI Adapter work (BYOM, Ollama). Resources are read-only and never mutate Meshery state. No credentials are exposed in logs or tool output.

3. Core decision: standalone process, first-class Meshery citizen

One question shapes the whole build: should the MCP server be written on the Meshery adapter framework, or standalone? This section takes a position.

The adapter framework makes Go binaries that register with Meshery Server, authenticate, and register capabilities. It exists for adapters that connect Meshery to infrastructure such as Istio or Kuma, and it manages the connection lifecycle between Meshery Server and the adapter.

The MCP server is a different kind of component. Its clients are AI agents, not Meshery Server. Its job is to translate the MCP protocol into Meshery API calls. The adapter registration lifecycle does not apply here, and building on it would couple the server to machinery it does not use and slow the project down.

Position: the MCP server is a standalone Go process that is a first-class Meshery citizen through conventions rather than through the adapter framework.

  • Go, matching the Meshery ecosystem.
  • MeshKit for error codes and structured logging, per the contributor guide.
  • Meshery schemas for connection, component, and relationship data.
  • A shared Meshery client as the only integration boundary.

SDK: mark3labs/mcp-go v0.57.0, already proven in the PR #28 scaffold with in-process client testing (Initialize, ListTools, CallTool in server_test.go). Chosen over the official go-sdk for its first-class tool registration and testability. Revisitable if the official SDK closes the gap.

Option Pros Cons
Adapter framework Uniform registration, built-in auth Built for data-plane adapters; its lifecycle does not fit a protocol server; slower to start
Fully standalone, no conventions Fastest to start Risks drifting out of the ecosystem
Position above Fast to start, first-class citizenship Requires discipline to stay on conventions

This position is open for debate. The alternative is worth attacking: if the MCP server should register with Meshery Server as an adapter, the counter-argument needs to show what the adapter lifecycle gives an AI-facing protocol server that the shared client does not.

4. System architecture

  • Transport: stdio for local agents, streamable HTTP for remote agents.
  • Server core: owns the MCP server, advertises capabilities, routes requests.
  • Registration interface: one contract for tools, resources, and prompts (section 5).
  • Implementations: internal/tools, internal/resources, internal/prompts.
  • Meshery client: REST and GraphQL wrapper with auth, timeout, and retry. The only integration boundary between MCP surfaces and Meshery.

5. Registration interface

Every surface area registers the same way. The scaffold already establishes this seam with tools.Register. This section makes it a shared contract.

The contract is one small Go interface with a single method:

type Registrant interface {
    Register(s *server.MCPServer) error
}
  • internal/tools exposes one Register for all tools.
  • internal/resources and internal/prompts follow the same shape.
  • A new tool is one file plus one line in Register.

A shared contract means contributors implement features consistently instead of each inventing their own pattern, and it gives reviewers one place to check registration.

6. Authentication and multi-tenancy

Tools receive an already-authenticated shared client. They never handle tokens, cookies, or credentials. Authentication lives in the client layer: token injection, context selection, refresh.

Named contexts support multiple Meshery instances (dev, staging, production) with context switching. Every call is authenticated and scoped, so an agent cannot act outside what the credential allows. No secrets are written to logs.

7. Transport and deployment

stdio ships in the scaffold and is the default for local agents (Claude Desktop, Cursor). Streamable HTTP follows (issue #5) with graceful shutdown, for remote agents.

The server runs as a local binary or a remote container. Multi-arch builds (linux/amd64, linux/arm64, darwin, windows) cover common platforms for local installs, since Go binaries must match the host architecture.

8. Meshery schemas and connection lifecycle

Tools that touch connections report schema-defined states (registered, connected, not found) and transition through them with intent rather than exiting or failing ad hoc. Errors use MeshKit error codes and structured logging, per the contributor guide. Meshery schemas are the source of truth for connection, component, and relationship data.

9. Surface catalog mapped to expected outcomes

LFX expected outcome MCP surface
Design lifecycle list_designs, get_design, create_design, deploy_design, undeploy_design
Cluster connections list_clusters, get_cluster, connect_cluster
Registry and models list_models, get_model, search_components
Environments and workspaces list_environments, list_workspaces
MeshSync topology, read-only resources: meshery://clusters/{id}/topology
Performance testing run_performance_test
Guided workflows prompts: deploy_application, design_review

10. Milestones

  1. Merge the scaffold (PR #28). Everything stacks on it.
  2. Shared Meshery client (issue #6) as the single integration boundary.
  3. list_designs (issue #30), the first read-only tool.
  4. Streamable HTTP transport (issue #5) with graceful shutdown.
  5. Registration interface landed as a shared contract.
  6. Environments, workspaces, clusters, registry, performance testing.
  7. Testing infrastructure (issue #16) and release automation (issue #15).

11. Open questions

  1. Should the MCP server register with Meshery Server as an adapter, and if so, what does that lifecycle give it that the shared client does not?
  2. Is internal/server the right home for the registration interface, or should it live elsewhere?
  3. Should the first tools land before streamable HTTP, or in parallel?

Feedback, corrections, and pushback are welcome on any section, especially section 3.

Section 3 — agree with the standalone position, and I’d add one concrete point in its favor from working in this codebase: the shared client (internal/meshery/client.go in #31) already has to solve auth (cookie vs bearer, provider config) independently of any adapter concerns. That auth logic doesn’t map cleanly onto the adapter registration lifecycle — adapters authenticate to Meshery Server as a peer component, but the MCP client is authenticating through Meshery Server on behalf of an external agent. Different shape of trust relationship. If the MCP server had to route through adapter registration, it’d need to reconcile “I’m an authenticated adapter” with “I’m relaying agent-scoped credentials,” which seems like exactly the kind of coupling section 3 is trying to avoid.

On the open question in section 3 — the counter-argument would need to show the adapter lifecycle gives multi-tenancy or context-switching (section 6’s named contexts) something the shared client can’t already provide on its own. From what I’ve seen in the client work so far, I don’t think it does.

Hi @AkashNaickar Thanks for publishing this. I implemented the initial Registrant seam in the scaffold, and the design maps well to what the first implementation needed.

What matched implementation

The small registration contract worked well in practice:

type Registrant interface {
    Register(*mcpserver.MCPServer) error
}

It kept MCP server construction focused: server.New() composes registrants, propagates registration failures, and startup stops cleanly if a surface cannot register. The approach also gave tests a direct way to cover successful and failed registration without requiring a live Meshery Server.

Keeping the contract in internal/server currently feels appropriate because it depends on the MCP server type and is owned by server construction. Tools, resources, and prompts can implement it without the server package importing their internal details.

Suggested clarifications

A few details are worth making explicit before more surfaces land:

  1. Composition ownership. The design should state that server construction owns the ordered list of registrants. Individual tools/resources/prompts should register themselves but should not construct the MCP server or register unrelated surfaces.

  2. Dependency injection. Registrants should receive narrow client/service interfaces through constructors. They should not create HTTP clients or read credentials themselves. That keeps the shared Meshery client as the integration boundary and makes tool tests independent of a live Meshery instance.

  3. Safety as registration metadata. The Registrant interface should remain small. Read-only/state-changing behavior belongs in the concrete MCP tool definition or registration metadata, so clients can discover it and enforce it not only in documentation.

  4. Error context. Registration should fail fast and return contextual errors identifying the surface that could not register, while preserving the original error for inspection.

  5. Avoid early generalization. One Registrant interface is enough for now. I would wait for a real resource and prompt implementation before introducing separate lifecycle hooks, registrant registries, or a broader framework.

Responses to the open questions

  1. Adapter framework or standalone process: I support the standalone-process
    position. The implemented Registrant seam is an MCP-server construction
    concern, while the shared Meshery client owns the Meshery-facing integration
    boundary. I do not see a demonstrated need for the data-plane adapter
    lifecycle in an AI-facing protocol server.

  2. Location of the registration interface: internal/server is the right
    home for now. The interface depends on the MCP server type, and server
    construction owns registrant composition and startup failure handling.
    Moving it to a more general package now would add indirection without a
    second use case.

  3. First tools versus streamable HTTP: Land the first read-only tool before
    streamable HTTP. A server_info or list_designs vertical slice will test
    the shared-client boundary, injected dependencies, tool registration,
    response mapping, read-only metadata, and error behavior. Streamable HTTP
    can then reuse a proven server/tool core rather than being built in parallel
    with unvalidated contracts.

    Overall, the standalone-process decision and shared-client boundary align with
    what the registrant implementation required. The next useful proof point is a
    first read-only tool, ideally server_info or list_designs, that receives an
    injected client dependency, registers through this seam, exposes read-only
    metadata in its MCP definition, and is tested with a fake client. That should
    land before streamable HTTP so the transport builds on a proven tool and client
    contract.