Understanding the Relationship Between MetaMCP Endpoints and Namespaces

MetaMCP endpoints are permanently bound to a single namespace through a UUID reference, with business logic enforcing that public endpoints can only reside in public namespaces.

The metatool-ai/metamcp repository implements a strict hierarchical relationship between MetaMCP endpoints and namespaces. Every endpoint record stores a namespaceUuid field that permanently associates it with its parent namespace, enabling consistent routing, permission enforcement, and visibility control throughout the system.

The Data Model: Storing Namespace References in Endpoints

Schema Definition in endpoints.zod.ts

The foundation of the relationship is defined in packages/zod-types/src/endpoints.zod.ts, where every endpoint schema includes a required namespaceUuid field. At line 12, the base schema defines namespaceUuid: z.string().uuid(), and this field propagates through all schema variations at lines 37, 61, and 134. This ensures that every endpoint creation, update, or retrieval operation must include a valid namespace UUID.

Business Logic Enforcement

Visibility Rules in endpoints.impl.ts

The implementation in apps/backend/src/trpc/endpoints.impl.ts enforces strict visibility consistency between endpoints and their namespaces. At line 63, the code validates that "public endpoints can only use public namespaces," throwing an error if a user attempts to create a public endpoint in a private namespace. This same guard appears at line 321 during update operations, preventing existing public endpoints from being moved into private namespaces.

Middleware Resolution in lookup-endpoint-middleware.ts

When processing incoming requests, apps/backend/src/middleware/lookup-endpoint-middleware.ts resolves the endpoint and injects its namespace UUID into the request context. At line 28, the middleware executes authReq.namespaceUuid = endpoint.namespace_uuid;, making the namespace identifier available to all downstream handlers for permission checks and routing decisions.

Runtime Routing and Proxy Behavior

Public MetaMCP Routes in streamable-http.ts

The public-facing proxy routes utilize the namespace relationship for session management. In apps/backend/src/routers/public-metamcp/streamable-http.ts, lines 117 and 128 extract the namespaceUuid from the authenticated endpoint and include it in session logs and context, ensuring that all traffic is properly attributed to its parent namespace.

Namespace-Level MCP Proxy in metamcp.ts

The architecture routes all endpoint traffic through its parent namespace. The proxy endpoint at apps/backend/src/routers/mcp-proxy/metamcp.ts line 87 defines the route /mcp-proxy/metamcp/:uuid, where :uuid represents the namespace identifier. Endpoints are never accessed in isolation; they are always reached through their owning namespace's proxy endpoint.

Practical Code Examples

Creating an Endpoint with Namespace Binding

When creating an endpoint via the TRPC client, you must specify the namespaceUuid:

import { createTRPCClient } from '@trpc/client';
import type { AppRouter } from '@metamcp/trpc';

const client = createTRPCClient<AppRouter>({
  url: '/trpc',
});

await client.mutation('endpoints.create', {
  name: 'weather-api',
  description: 'Expose weather data',
  // The namespace this endpoint belongs to
  namespaceUuid: 'a1b2c3d4-5678-90ab-cdef-1234567890ab',
});

The namespaceUuid field is validated by the Zod schema in packages/zod-types/src/endpoints.zod.ts (lines 12-14).

Resolving Namespace Context in Middleware

After the lookup middleware processes a request, the namespace UUID is available in the authentication context:

// In a request handler after the lookup-endpoint middleware
export async function handleEndpointMcp(req: Request) {
  // namespaceUuid is injected by the middleware (see line 28)
  const nsUuid = req.authReq.namespaceUuid;

  // Fetch namespace configuration for permission checks
  const namespace = await namespacesRepository.findByUuid(nsUuid);
  // Enforce namespace-level policies or route to the correct MCP server
}

Enforcing Visibility Constraints

The backend prevents visibility mismatches between endpoints and namespaces:

if (input.public && namespace.private) {
  throw new PermissionError(
    `Public endpoints can only use public namespaces. Namespace "${namespace.name}" is private`,
  );
}

This validation appears at line 63 of apps/backend/src/trpc/endpoints.impl.ts for creation operations and line 321 for updates.

Key Files Reference

File Role
packages/zod-types/src/endpoints.zod.ts Zod schema defining the namespaceUuid field for all endpoint records.
apps/backend/src/trpc/endpoints.impl.ts Business logic enforcing visibility rules between endpoints and namespaces.
apps/backend/src/middleware/lookup-endpoint-middleware.ts Middleware that resolves endpoints and injects namespaceUuid into request context.
apps/backend/src/routers/public-metamcp/streamable-http.ts Public proxy routes utilizing namespace UUID for session management.
apps/backend/src/routers/mcp-proxy/metamcp.ts Namespace-level MCP proxy router where endpoints are accessed via their parent namespace.
apps/backend/src/trpc/namespaces.impl.ts Namespace data access and permission helpers used by endpoint logic.

Summary

  • Every endpoint stores a namespaceUuid in its data model, creating a permanent parent-child relationship defined in packages/zod-types/src/endpoints.zod.ts.
  • Visibility rules are strictly enforced: public endpoints can only exist in public namespaces, with validation occurring in apps/backend/src/trpc/endpoints.impl.ts at lines 63 and 321.
  • Runtime resolution injects the namespace UUID into request contexts via apps/backend/src/middleware/lookup-endpoint-middleware.ts (line 28), enabling downstream permission checks.
  • All endpoint traffic routes through the parent namespace, whether via public MetaMCP streams or the namespace-level MCP proxy at /mcp-proxy/metamcp/:uuid.

Frequently Asked Questions

Can an endpoint exist without a namespace in MetaMCP?

No. The Zod schema in packages/zod-types/src/endpoints.zod.ts requires every endpoint to have a namespaceUuid field (lines 12, 37, 61, and 134). The database layer and business logic both treat this as a mandatory foreign key relationship, making namespaces a required parent entity for all endpoints.

What happens if I try to create a public endpoint in a private namespace?

The system prevents this operation and throws a permission error. In apps/backend/src/trpc/endpoints.impl.ts, lines 63 and 321 contain guards that check if input.public is true while namespace.private is true, throwing a PermissionError with the message "Public endpoints can only use public namespaces" to maintain visibility consistency.

How does the middleware determine which namespace an endpoint belongs to?

The lookup-endpoint-middleware.ts resolves the endpoint by its identifier and extracts the namespace_uuid field from the database record. At line 28, it assigns this value to authReq.namespaceUuid, injecting the namespace UUID into the request context before any business logic handlers execute, making it available for downstream permission checks and routing decisions.

Are endpoints accessed directly by their own UUID or through their namespace?

Endpoints are always accessed through their parent namespace, never in isolation. The MCP proxy router in apps/backend/src/routers/mcp-proxy/metamcp.ts (line 87) defines the route pattern /mcp-proxy/metamcp/:uuid, where the UUID parameter represents the namespace identifier. Public streaming endpoints similarly derive the namespace UUID from the endpoint record to route traffic correctly through the namespace proxy.

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 →