MetaMCP Namespace System: Grouping and Configuring MCP Servers

MetaMCP namespaces function as logical containers that aggregate one or more MCP servers into a single endpoint, enabling private or public visibility controls, per-tool metadata overrides, and centralized lifecycle management through a type-safe TRPC API.

The namespace system in the metatool-ai/metamcp repository provides the organizational foundation for treating multiple MCP servers as cohesive units. By implementing Zod-validated schemas and database persistence in @metamcp/zod-types, this system allows administrators to group servers, customize tool presentations, and enforce access boundaries without modifying upstream server configurations.

Namespace Data Model and Schema Architecture

At the core of the system are four interconnected entities defined in packages/zod-types/src/namespaces.zod.ts:

  • NamespaceSchema – The root container with fields uuid, name, description, and user_id (nullable). When user_id is null, the namespace is public; otherwise, it is private to that user.
  • NamespaceServer – Extends McpServerSchema with namespace-specific state including status and optional error_status fields.
  • NamespaceTool – Extends ToolSchema with serverName, serverUuid, status, and override fields (overrideName, overrideTitle, overrideDescription, overrideAnnotations).
  • DatabaseNamespace – The raw database representation where timestamp fields are native Date objects rather than strings.

All incoming API payloads for creation, updates, status changes, and tool overrides are validated against these schemas before processing.

Backend Implementation and Business Logic

The concrete enforcement of namespace rules resides in apps/backend/src/trpc/namespaces.impl.ts. The create procedure determines the effective owner through effectiveUserId and validates security constraints:

const isPublicNamespace = effectiveUserId === null;
// Validation ensures public namespaces cannot contain private MCP servers

Key implementation details include:

  • Creation validation – Prevents public namespaces from referencing private MCP servers to avoid configuration leakage.
  • Access control – The findAllAccessibleToUser method returns both the requesting user’s private namespaces and all public namespaces.
  • Status management – Separate mutation paths for updateServerStatus and updateToolStatus allow granular lifecycle control.
  • Metadata overrides – Permits renaming tools and attaching arbitrary annotations without altering the upstream server definition.

TRPC API Surface and Available Procedures

The frontend-facing router in packages/trpc/src/routers/frontend/namespaces.ts exposes ten protected procedures:

  • list – Queries accessible namespaces using ListNamespacesResponseSchema.
  • get – Retrieves a specific namespace by UUID via GetNamespaceResponseSchema.
  • getTools – Fetches tools within a namespace using GetNamespaceToolsRequestSchema.
  • create – Creates a new namespace with CreateNamespaceRequestSchema.
  • update – Modifies namespace properties via UpdateNamespaceRequestSchema.
  • updateServerStatus – Toggles server states using UpdateNamespaceServerStatusRequestSchema.
  • updateToolStatus – Controls individual tool availability.
  • updateToolOverrides – Applies custom metadata with UpdateNamespaceToolOverridesRequestSchema.
  • refreshTools – Synchronizes tool lists from underlying servers using RefreshNamespaceToolsRequestSchema.
  • delete – Removes a namespace and its associations.

Public vs Private Namespace Configuration

Namespaces support two visibility modes controlled by the user_id field:

  • Private namespaces contain a valid user UUID in user_id, restricting list, edit, and retrieval operations to that owner and system administrators.
  • Public namespaces set user_id to null, making them readable and usable by anyone, though modification remains restricted to owners and admins.

During creation, the backend enforces a strict relationship rule: public namespaces cannot contain private MCP servers. This validation occurs in namespaces.impl.ts (lines 77-82) to ensure that private server configurations never leak through public endpoints.

Tool Overrides and Annotations

Namespaces enable UI-level customization of tool metadata without requiring changes to the source MCP server. The NamespaceToolSchema supports four override fields:

overrideName?: string | null;
overrideTitle?: string | null;
overrideDescription?: string | null;
overrideAnnotations?: Record<string, unknown> | null;

These overrides merge at request time, allowing teams to add contextual hints such as readOnlyHint: false or rename cryptic tool identifiers into human-readable labels for specific use cases.

Practical Configuration Examples

Creating a Private Namespace

const resp = await trpc.namespaces.create.mutate({
  name: "InternalAnalytics",
  description: "Private data processing tools",
  mcpServerUuids: ["srv-uuid-1", "srv-uuid-2"],
  // user_id defaults to calling user, making this private
});

Creating a Public Namespace

await trpc.namespaces.create.mutate({
  name: "OpenUtilities",
  description: "Community-shared utilities",
  mcpServerUuids: ["public-srv-uuid"],
  user_id: null,  // Explicitly public
});

The backend rejects this request if any server in mcpServerUuids is private.

Listing Accessible Namespaces

const { data } = await trpc.namespaces.list.query();
const summary = data?.map(ns => ({
  uuid: ns.uuid,
  name: ns.name,
  isPublic: ns.user_id === null,
}));

Updating Tool Overrides

await trpc.namespaces.updateToolOverrides.mutate({
  namespaceUuid: "ns-uuid",
  toolUuid: "tool-uuid",
  serverUuid: "server-uuid",
  overrideName: "Generate Monthly Report",
  overrideAnnotations: { readOnlyHint: false },
});

Refreshing Tools After Server Changes

await trpc.namespaces.refreshTools.mutate({
  namespaceUuid: "ns-uuid",
  tools: []  // Empty array triggers full refresh from MetaMCP connection
});

Summary

  • Namespaces aggregate multiple MCP servers under a single UUID-identified endpoint with database persistence.
  • Visibility is determined by the user_id field; null indicates public access while a UUID restricts the namespace to that owner.
  • Security enforcement prevents public namespaces from containing private MCP servers, implemented in apps/backend/src/trpc/namespaces.impl.ts.
  • Tool customization occurs through overrideName, overrideTitle, overrideDescription, and overrideAnnotations fields stored in NamespaceToolSchema.
  • API coverage includes ten TRPC procedures for CRUD operations, status management, and metadata overrides.
  • Schema validation uses Zod definitions in packages/zod-types/src/namespaces.zod.ts to ensure type safety across the stack.

Frequently Asked Questions

How do public and private namespaces differ in MetaMCP?

Private namespaces bind to a specific user via the user_id field, limiting access to that user and administrators. Public namespaces set user_id to null, allowing anyone to list and invoke tools while restricting modifications to authorized owners. The backend explicitly validates that public namespaces cannot reference private MCP servers to maintain security boundaries.

Where can you customize tool metadata without modifying the upstream server?

Use the updateToolOverrides mutation targeting packages/trpc/src/routers/frontend/namespaces.ts. This accepts overrideName, overrideTitle, overrideDescription, and overrideAnnotations parameters defined in UpdateNamespaceToolOverridesRequestSchema, storing them within the namespace-specific tool record for runtime merging.

What prevents private server configurations from leaking through public namespaces?

The create procedure in apps/backend/src/trpc/namespaces.impl.ts checks effectiveUserId to determine if the namespace is public (null). If so, it validates that all referenced mcpServerUuids correspond to public servers, rejecting the transaction if any private server is detected in the group.

Which procedure synchronizes tool definitions after adding servers to a namespace?

The refreshTools mutation triggers synchronization with underlying MCP servers. When invoked with an empty tools array and a valid namespaceUuid, it pulls current tool definitions from the MetaMCP connection and updates the namespace-tool mapping table according to RefreshNamespaceToolsRequestSchema.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →