Skip to main content
Glama

Kurd MCP

PyPI Python License: MIT CI

Kurd is a high-performance Model Context Protocol (MCP) gateway for Python, powered by Rust.

The Rust data plane handles HTTP serving, JSON-RPC dispatch, tool routing, upstream aggregation, caching, retries, circuit breaking, backpressure, rate limiting, and Prometheus metrics. The Python layer provides the developer API — tool registration, runtime configuration, and an optional enterprise feature set.

Targets MCP protocol revision 2026-07-28. Fully typed (PEP 561).


Contents


Related MCP server: MCPilot

Installation

pip install kurd

Requires Python 3.10+ and a 64-bit platform. Pre-built wheels are available for Windows, Linux (x86-64, aarch64), and macOS (x86-64, Apple Silicon).


Quick Start

from kurd import Router
from kurd._kurd import start_http_gateway

router = Router()

@router.tool()
async def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b

# Blocks until stop_http_gateway() is called or the process exits.
start_http_gateway("0.0.0.0:9200")

The gateway starts the following endpoints:

Path

Method

Purpose

/mcp

POST

JSON-RPC 2.0 MCP endpoint

/health

GET

Liveness probe — returns 200 OK

/status

GET

Runtime, cache, upstream, and circuit-breaker snapshot

/metrics

GET

Prometheus metrics

/admin/servers

GET

List registered upstream servers

/admin/servers

POST

Add or replace an upstream server

/admin/servers/{name}

DELETE

Remove an upstream server

/admin/tools

GET

List all tools (local + upstream) with source label

/admin/tools/reload

POST

Expire the tool-list cache immediately

/admin/tools/namespaces

GET

List upstream namespaces

Call the gateway:

curl -s http://localhost:9200/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"add","arguments":{"a":3,"b":4}}}'
{"jsonrpc":"2.0","id":1,"result":{"resultType":"complete","content":[{"type":"text","text":"7"}],"isError":false}}

CLI

Kurd ships a kurd command installed alongside the package.

Usage: kurd <COMMAND>

Commands:
  serve   Start the HTTP MCP gateway

Options:
  -h, --help  Show this message and exit

kurd serve

kurd serve [--host HOST] [--port PORT] [--token TOKEN]

Flag

Default

Description

--host

0.0.0.0

Bind address

--port

8000

Bind port

--token

Bearer token for authentication (overrides KURD_AUTH_TOKEN)

# Start on port 8000 with no authentication
kurd serve

# Start on a specific address with a bearer token
kurd serve --host 127.0.0.1 --port 9200 --token my-secret

# Use an environment variable for the token
KURD_AUTH_TOKEN=my-secret kurd serve --port 9200

Registering Tools

Decorator API

from kurd import Router

router = Router()

@router.tool()
async def search(query: str, limit: int = 10) -> list[str]:
    """Search the knowledge base."""
    return [f"result {i}" for i in range(limit)]

Type annotations are converted to a JSON Schema inputSchema automatically:

Python type

JSON Schema type

int

integer

float

number

bool

boolean

str

string

list[T]

array with item schema

dict

object

Optional[T] / T | None

schema of T

Parameters with defaults become optional; parameters without defaults are added to required.

Hot-reloading

Replace a tool's implementation at runtime without restarting the gateway:

router.reload_tool("search", new_search_function)

Unregistering

router.unregister_tool("search")

Introspection

router.list_tools()      # -> ["add", "search", ...]
router.list_upstreams()  # -> [("github", "http://..."), ...]

Mounting Upstream Servers

Kurd aggregates remote MCP servers alongside local tools.

router.mount("github", "http://github-mcp.internal:9300")
router.mount("jira",   "http://jira-mcp.internal:9300")

Upstream tools are prefixed with the upstream name:

github.create_issue
jira.create_ticket

Clients discover all tools — local and upstream — through a single tools/list call. Kurd fetches remote tool lists concurrently, caches them with a configurable TTL, and follows pagination automatically.

Unmounting and cache invalidation

router.unmount("github")   # stop routing to this upstream
router.refresh_tools()     # expire the tool list cache immediately

Upstream behaviour

  • Connection pool: persistent HTTP/1.1 connections via Reqwest

  • Retry: up to 3 attempts with exponential backoff + jitter

  • Circuit breaker: opens after 5 consecutive failures; resets after 30 s

  • Timeout: configurable per RuntimeConfig.upstream_timeout_ms

  • Private-network policy: loopback/private URLs blocked by default unless set_allow_private_upstreams(True) is called


Tool Discovery Filtering

Clients can scope a tools/list call with an optional filter parameter — without any server-side configuration needed.

Namespace filter

Returns only tools belonging to a specific upstream:

{
  "jsonrpc": "2.0", "id": 1, "method": "tools/list",
  "params": { "filter": { "namespace": "github" } }
}

Search filter

Case-insensitive substring match across tool name and description:

{
  "jsonrpc": "2.0", "id": 1, "method": "tools/list",
  "params": { "filter": { "search": "file" } }
}

Discovery metadata

Every tools/list response includes a _kurd object:

{
  "result": {
    "tools": [...],
    "_kurd": { "available": 12, "returned": 3 }
  }
}

available is the count after tenant restrictions; returned is the count after the client filter. An LLM agent can use these counts to know whether to refine its query.

Security: client filters always run after per-tenant restrictions. A tenant cannot use search or namespace to enumerate tools outside their allowlist.


Admin API

The Admin API lets operators manage the gateway at runtime without a restart. All admin endpoints accept an optional Authorization: Bearer <token> header.

Set a dedicated admin token

router.set_admin_token("admin-secret")
# router.clear_admin_token()  # fall back to MCP bearer token / open

Or via the module-level API:

from kurd import set_admin_token, clear_admin_token
set_admin_token("admin-secret")

Manage upstream servers

# List
curl http://localhost:9200/admin/servers

# Add / replace
curl -X POST http://localhost:9200/admin/servers \
  -H 'Content-Type: application/json' \
  -d '{"name": "github", "url": "http://github-mcp.internal:9300/mcp"}'

# Remove
curl -X DELETE http://localhost:9200/admin/servers/github

Response codes: 201 Created (new), 200 OK (replaced), 404 Not Found (delete miss), 400 Bad Request (invalid URL or empty name).

Inspect tools

# All tools with source label
curl http://localhost:9200/admin/tools

# Reload (expire cache)
curl -X POST http://localhost:9200/admin/tools/reload

# List upstream namespaces
curl http://localhost:9200/admin/tools/namespaces

Runtime Configuration

All gateway tunables are collected in RuntimeConfig:

from kurd import Router, RuntimeConfig

router = Router()
router.configure_runtime(RuntimeConfig(
    # Concurrency
    global_concurrency   = 512,   # max simultaneous in-flight requests
    upstream_concurrency = 64,    # max simultaneous upstream calls
    python_concurrency   = 64,    # max simultaneous Python tool calls
    upstream_timeout_ms  = 30_000,

    # Logging
    request_logging      = False, # structured per-request log lines

    # Rate limiting
    rate_limiting_enabled   = True,
    rate_limit_per_ip_rps   = 1_000,
    rate_limit_global_rps   = 10_000,

    # IP allowlist (None = allow all)
    ip_allowlist         = ["192.168.1.0/24", "10.0.0.1"],

    # Tool cache
    tools_cache_ttl_ms   = 30_000,

    # Enterprise (all off by default)
    enable_dlq                  = False,
    enable_idempotency          = False,
    secrets_backend             = "env",
    enable_webhooks             = False,
    enable_distributed_state    = False,
    distributed_state_backend   = "memory",
    redis_url                   = "redis://localhost:6379/0",
    enable_distributed_tracing  = False,
))

configure_runtime also accepts keyword arguments directly for ergonomic one-liners:

router.configure_runtime(request_logging=True, rate_limiting_enabled=True)

Runtime status

status = router.runtime_status()
# {
#   "global_active": 3,
#   "global_limit": 512,
#   "python_active": 1,
#   "upstream_metrics": {...},
#   "cache": {"hits": 142, "misses": 3},
#   ...
# }

Security

Bearer token authentication

Set a bearer token before starting the gateway. Requests missing or carrying a wrong token receive 401 Unauthorized.

from kurd._kurd import set_http_bearer_token, clear_http_bearer_token

set_http_bearer_token("my-production-token")
# clear_http_bearer_token()  # disable authentication

Via environment variable (loaded automatically at gateway start):

KURD_AUTH_TOKEN=my-production-token kurd serve

Tokens are compared with a constant-time byte comparison to prevent timing attacks.

IP allowlist

from kurd import set_ip_allowlist, clear_ip_allowlist

set_ip_allowlist(["10.0.0.1", "10.0.0.2"])
clear_ip_allowlist()  # allow all IPs again

Or through RuntimeConfig.ip_allowlist. Blocked IPs receive 403 Forbidden.

Rate limiting

router.configure_runtime(
    rate_limiting_enabled=True,
    rate_limit_per_ip_rps=1_000,
    rate_limit_global_rps=10_000,
)

Rate-limited requests receive 429 Too Many Requests with a Retry-After: 1 header and a retryAfterMs field in the JSON-RPC error body.

Additional safeguards

Safeguard

Details

Request size cap

1 MiB hard limit; 413 on excess

Content-type validation

Must be application/json; -32600 otherwise

Upstream URL validation

Rejects credentials, fragments, and unsupported schemes

Private-network policy

Upstream calls to loopback/RFC1918 blocked by default

CORS

OPTIONS /mcp returns correct preflight headers; POST responses include Access-Control-Allow-Origin: *

Overload rejection

503 when global concurrency limit is reached

For internet-facing deployments, terminate TLS at a reverse proxy (nginx, Caddy, AWS ALB) and apply network-level controls there.


Multi-tenancy & Policy Engine

Kurd ships a built-in TenantManager that wires directly into the Rust request hot path. One call to set_policy_engine() activates both tools/call gating and tools/list filtering simultaneously.

Basic setup

from kurd import Router, TenantManager

manager = TenantManager()

# Add tenants with explicit tool allowlists
manager.add_tenant("acme",   name="Acme Corp",  allowed_tools=["add", "search"], api_key="sk-acme")
manager.add_tenant("devops", name="DevOps Team", allowed_tools=["*"],            api_key="sk-ops")

router = Router()
router.set_policy_engine(manager)
# router.clear_policy_engine()  # disable, all requests allowed again

What it enforces

Behaviour

Details

tools/call gating

Unknown API key or tool outside allowlist → 403 Forbidden (JSON-RPC -32004)

tools/list filtering

Response contains only the tools the caller may invoke

Wildcard support

"*" in allowed_tools passes all tools through

Namespace wildcard

"github.*" passes all tools prefixed github.

Unknown key

Returns an empty tools list and 403 on any tools/call

Calling with a tenant key

curl http://localhost:9200/mcp \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer sk-acme' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# → returns only ["add", "search"]

Prometheus metric

kurd_policy_denied_total   # counter — requests blocked by the policy engine

Per-tenant quotas and billing

For rate-per-tenant quotas, request tracking, and billing see the Enterprise Features section.


Observability

Structured logging

Enable per-request log lines (goes to stdout in the format chosen by KURD_LOG):

router.configure_runtime(request_logging=True)

Control log verbosity via environment variable:

KURD_LOG=kurd=debug kurd serve   # debug, info, warn, error
RUST_LOG=info kurd serve         # fallback if KURD_LOG is unset

Log level can also be changed at runtime via the logging/setLevel MCP method.

Prometheus metrics

curl http://localhost:9200/metrics

Metric

Type

Description

kurd_requests_total{status}

counter

Total requests by status (total, completed, rejected)

kurd_policy_denied_total

counter

Requests blocked by the policy engine (-32004)

kurd_requests_active

gauge

In-flight requests right now

kurd_requests_peak_active

gauge

Highest concurrent request count since startup

kurd_request_latency_ms

gauge

Rolling average latency (ms)

kurd_request_latency_histogram_ms_bucket{le}

histogram

Latency distribution (1ms … 5000ms + Inf)

kurd_request_latency_histogram_ms_count

counter

Total completed requests counted in histogram

kurd_request_latency_histogram_ms_sum

counter

Total latency (ms) summed across all requests

kurd_python_active_calls

gauge

Active Python tool invocations

kurd_python_peak_active_calls

gauge

Peak simultaneous Python tool invocations

kurd_python_rejections_total

counter

Python tool calls dropped due to concurrency limit

kurd_upstream_requests_total{upstream}

counter

Requests forwarded per upstream

kurd_upstream_successes_total{upstream}

counter

Successful upstream calls

kurd_upstream_failures_total{upstream}

counter

Failed upstream calls

kurd_upstream_retries_total{upstream}

counter

Retry attempts per upstream

kurd_upstream_latency_ms{upstream}

gauge

Average upstream round-trip latency

kurd_upstream_circuit_breaker_state{upstream}

gauge

0 = closed, 1 = open

kurd_upstream_active_calls{upstream}

gauge

Current in-flight calls per upstream

kurd_upstream_peak_active_calls{upstream}

gauge

Peak in-flight calls per upstream

kurd_upstream_rejections_total

counter

Upstream calls dropped due to concurrency limit

kurd_cache_hits_total

counter

Tool-list cache hits

kurd_cache_misses_total

counter

Tool-list cache misses

kurd_cache_invalidations_total

counter

Cache invalidations (manual or TTL expiry)

kurd_concurrency_limit{type}

gauge

Configured limits: global, upstream, python

Prometheus scrape config

# prometheus.yml
scrape_configs:
  - job_name: kurd
    static_configs:
      - targets: ["localhost:9200"]
    metrics_path: /metrics
    scrape_interval: 15s

Datadog

# datadog.yaml
instances:
  - openmetrics_endpoint: http://localhost:9200/metrics
    namespace: kurd
    metrics: ["kurd_.*"]

OpenTelemetry

Kurd exports real OTLP spans from the Rust core — no Python OpenTelemetry SDK required.

Quick setup

from kurd import Router
from kurd.telemetry import setup_otel

router = Router()

# Activates Rust-side OTLP export. setup_otel returns an OTELTracer for
# any additional Python-side instrumentation you want.
setup_otel(
    service_name = "my-gateway",
    endpoint     = "http://otel-collector:4318",  # OTLP HTTP receiver
)

Or directly via the Router:

router.configure_otel("http://otel-collector:4318", service_name="my-gateway")
# router.clear_otel()  # disable export

What gets traced

  • Every request that passes authentication, rate limiting, and concurrency checks produces one server-side span.

  • Spans are exported fire-and-forget (2-second timeout, errors silently dropped) so a slow or unavailable collector never adds latency.

  • The OTLP JSON payload is sent to {endpoint}/v1/traces.

W3C traceparent propagation

Every MCP response carries a traceparent header so downstream services and LLM agents can continue the trace:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

If the incoming request already carries a traceparent, Kurd preserves the trace_id and issues a new span_id. Malformed headers start a fresh trace.

OTELConfig.service_version defaults to the installed kurd package version automatically.

Health checks

from kurd.health_checks import HealthCheckManager

hc = HealthCheckManager()

# Register a custom check
async def check_db():
    ...

hc.register_check("database", check_db, critical=True)

# Kubernetes probes
readiness = await hc.check_readiness()  # all critical checks pass
liveness  = await hc.check_liveness()   # process is running and active

MCP Protocol Compliance

Kurd implements the MCP 2026-07-28 protocol revision.

Supported methods

Method

Behaviour

initialize

Returns protocolVersion, capabilities, and serverInfo

ping

Returns {}

server/discover

Returns capabilities, supported versions, and server identity

tools/list

Aggregates local + upstream tools; cursor-based pagination; params.filter.namespace / params.filter.search; _kurd metadata; per-tenant filtering when policy engine is active

tools/call

Routes to local Python tool or upstream server; policy engine gate when active

resources/list

Returns empty list with ttlMs and cacheScope

resources/read

Returns {"contents": []}

prompts/list

Returns empty list with ttlMs and cacheScope

prompts/get

Returns -32602 (gateway holds no prompts)

completion/complete

Returns {"values": [], "hasMore": false}

logging/setLevel

Applies log level to the tracing filter at runtime

notifications/*

Accepted silently — returns 202 Accepted with empty body

Modern HTTP headers

When a client sends Mcp-Protocol-Version: 2026-07-28, Kurd additionally validates:

  • Mcp-Method header matches the JSON-RPC method field

  • Mcp-Name header matches params.name for tools/call

Mismatched headers return -32020. Unsupported protocol versions return -32019.


Enterprise Features

Enable features through RuntimeConfig or by importing the relevant manager class directly.

Multi-tenancy

See Multi-tenancy & Policy Engine above for the full policy engine and tool-filtering setup.

from kurd import TenantManager

manager = TenantManager()
manager.add_tenant(
    tenant_id="acme",
    name="Acme Corp",
    quota_rps=100,
    allowed_tools=["add", "search"],
    api_key="sk-acme",
)

Each tenant receives a unique API key. Quotas and tool ACLs are enforced independently.

Billing

from kurd.billing import BillingManager

billing = BillingManager()
billing.set_pricing({"add": {"per_call": 0.001, "per_latency_ms": 0.0001}})
billing.track_call(tenant_id="acme", tool_name="add", latency_ms=12.5, success=True)

report = billing.get_usage_report("acme", period="2026-08")

Supported models: per-request, per-latency, tiered, hybrid.

Request idempotency

router.configure_runtime(enable_idempotency=True)
mgr = router.get_idempotency()

is_dup, cached = mgr.check_idempotent_key("req-abc-123", tenant_id="acme")
if is_dup:
    return cached

result = run_tool()
mgr.store_result("req-abc-123", "acme", result)

Backed by SQLite with a 24-hour TTL.

Dead-letter queue

router.configure_runtime(enable_dlq=True, dlq_storage_path="/data/kurd/dlq")
dlq = router.get_dlq()

dlq.add_message(request_id="req-123", tenant_id="acme",
                tool_name="add", arguments={"a":1,"b":2}, error="timeout")

dlq.register_replay_handler("add", handler)
success, error = dlq.replay_message("dlq_abc123")

stats = dlq.get_statistics(tenant_id="acme")
dlq.cleanup_archived(days=30)

Replay uses exponential backoff up to 1 hour.

Secrets management

from kurd.secrets_management import SecretsManager

# Kubernetes in-cluster | HashiCorp Vault | AWS Secrets Manager | env (default)
manager = SecretsManager(backend="vault",
                          vault_addr="https://vault.example.com",
                          vault_token="s.xxxxx")

secret = manager.get_secret("db_password")

Third-party dependencies (kubernetes, hvac, boto3) are imported lazily — only when the matching backend is activated.

Webhooks

router.configure_runtime(enable_webhooks=True)
hooks = router.get_webhooks()

hooks.register_webhook(
    url="https://example.com/hooks",
    events=["error", "rate_limit_exceeded"],
    tenant_id="acme",
)

Deliveries are HMAC-SHA256 signed and logged for audit via get_deliveries().

Distributed state

router.configure_runtime(
    enable_distributed_state=True,
    distributed_state_backend="redis",
    redis_url="redis://localhost:6379/0",
)
state = router.get_distributed_state()
state.set("gateway:version", 42)
state.increment("counters:acme:calls")

Use backend="memory" for single-instance deployments.

Distributed tracing

from kurd.distributed_tracing import extract_context

trace = extract_context(incoming_headers)
span  = trace.create_span("tool_execution", {"tool": "add"})
span.set_attribute("result", 42)
span.end()

Follows W3C Trace Context. Tracing state is available in router.runtime_status() when enabled.


Performance

Benchmarks from a Windows development machine (Python 3.12, release build):

Scenario

Concurrency

Throughput

p50

p95

p99

Errors

Local Python tool

10

594.5 req/s

14.9 ms

23.9 ms

28.7 ms

0%

Local Python tool

50

587.9 req/s

33.3 ms

87.8 ms

119.1 ms

0%

Local Python tool

100

556.0 req/s

18.3 ms

29.5 ms

32.4 ms

0%

Upstream tool

10

412.2 req/s

21.8 ms

36.4 ms

42.7 ms

0%

Upstream tool

50

229.8 req/s

20.6 ms

534.6 ms

549.3 ms

0%

Sustained burst

100

573.3 req/s

73.5 ms

179.1 ms

218.5 ms

0%

Results depend on hardware, OS, Python version, and network conditions.

python -m pytest tests/test_load.py -q -s

Architecture

Python application
       │
       ▼
  kurd.Router                      ← Python API layer
       │
       ├── Policy engine (set_policy_engine)
       │   TenantManager callbacks wired into Rust hot path
       │
       ├── Enterprise modules (optional, lazy)
       │   multitenancy · billing · idempotency · DLQ
       │   secrets · webhooks · distributed state
       │
       ▼
   PyO3 boundary
       │
       ▼
  Rust MCP gateway (Axum + Tokio)
       │
       ├── HTTP handler  ─────────────────────────────────┐
       │   content-type · auth · IP allowlist              │
       │   rate limiting · concurrency backpressure        │
       │   CORS · request ID · W3C traceparent             │
       │   OTLP span export (fire-and-forget)              │
       │                                                   │
       ├── Admin API (/admin/*)                            │
       │   server CRUD · tool listing · cache reload       │
       │                                                   │
       ├── MCP dispatcher                                  │
       │   initialize · ping · server/discover             │
       │   tools/list (paginated, filtered, _kurd meta)    │
       │   tools/call (policy gate) · resources · prompts  │
       │   completion · logging · notifications (202)      │
       │                                                   │
       ├── Local Python tools ◄── PyO3 callback            │
       │   (Rayon-parallel batch parsing)                  │
       │                                                   │
       └── Upstream MCP servers                           │
           retry · circuit breaker · cache · metrics      ◄┘

The Rust layer holds all mutable gateway state in lock-free atomics and RwLock-guarded maps. Python code never touches the hot path after registration.


Development

Prerequisites

  • Rust stable toolchain (rustup update stable)

  • Python 3.10+

  • maturin and pytest

pip install maturin pytest

Build

# Development build (fast iteration)
maturin develop

# Optimised build (benchmarks, pre-release testing)
maturin develop --release

# Release wheel
maturin build --release

Test

python -m pytest -q

The test suite covers:

  • JSON-RPC parsing and fast batch parsing (Rayon)

  • Local sync and async tools

  • initialize handshake and lifecycle methods

  • tools/list pagination

  • completion/complete, notifications/202, CORS preflight

  • Upstream discovery, routing, and concurrency

  • Circuit breaker, retry, and timeout behaviour

  • Tool-list cache hits, misses, and invalidation

  • Bearer authentication (accepted and rejected)

  • IP allowlist enforcement

  • Rate-limit rejection and Retry-After header

  • Request-size and content-type guards

  • Prometheus metrics output

  • Load and burst behaviour

  • Policy engine: allow, deny, unknown key, clear (P0)

  • Admin API: server CRUD, tool listing, reload, auth token (P1)

  • Per-tenant tool filtering: wildcard, restricted, unknown key, clear (P2)

  • Client tool discovery: namespace filter, search, combined, _kurd metadata, bypass prevention (P3)

  • OpenTelemetry: traceparent presence/format, trace-id propagation, span-id rotation, malformed input, enable/disable (P4)

Linting

cargo check
cargo clippy -- -D warnings

Environment variables

Variable

Purpose

KURD_AUTH_TOKEN

Bearer token loaded automatically at gateway start

KURD_LOG

Tracing filter (e.g. kurd=debug). Takes precedence over RUST_LOG

RUST_LOG

Standard Rust log filter fallback


Project Structure

kurd-mcp/
├── kurd/
│   ├── __init__.py            # Public API + __version__
│   ├── py.typed               # PEP 561 marker
│   ├── cli.py                 # `kurd serve` entry point
│   ├── router.py              # Router class + RuntimeConfig
│   ├── telemetry.py           # OpenTelemetry integration
│   ├── health_checks.py       # Readiness and liveness probes
│   ├── authorization.py       # RBAC helpers
│   ├── multitenancy.py
│   ├── billing.py
│   ├── idempotency.py
│   ├── dead_letter_queue.py
│   ├── secrets_management.py
│   ├── webhooks.py
│   ├── distributed_state.py
│   ├── distributed_tracing.py
│   └── ...
├── src/
│   └── lib.rs                      # Rust data plane (~3500 lines)
├── tests/
│   ├── test_core.py
│   ├── test_http_gateway.py        # Integration tests (module-scoped gateway)
│   ├── test_admin_api.py           # P1 — Admin HTTP API
│   ├── test_tool_filtering.py      # P2 — Per-tenant tools/list filtering
│   ├── test_tool_discovery.py      # P3 — Client-requested filter + _kurd metadata
│   ├── test_otel.py                # P4 — traceparent / OTLP export
│   ├── test_upstream.py
│   ├── test_load.py
│   ├── test_prometheus_metrics.py
│   └── upstream_server.py          # In-process upstream fixture
├── Cargo.toml
├── pyproject.toml
├── LICENSE
└── README.md

Contributing

Issues and pull requests are welcome via the GitHub repository.

Before submitting:

cargo check
cargo clippy -- -D warnings
maturin develop --release
python -m pytest -q

Please open an issue before starting large changes.


License

MIT — Copyright © 2024 Semko Kermashani


The name Kurd honors Kurdish identity and heritage. Bezhi Kurd u Kurdistan.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    C
    quality
    D
    maintenance
    A powerful gateway for the Model Context Protocol (MCP) that unifies AI toolchains by federating multiple MCP servers, wrapping REST APIs as MCP tools, and supporting multiple transport methods with an admin dashboard.
    1
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables building lightweight MCP servers where Rust handles transport, routing, and schema validation while Python defines tool behavior using type hints, with support for stdio and HTTP transports.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Semantic tool-routing gateway for MCP servers that cuts 70-90% of context tokens, with RBAC, API-key auth, response caching, hot-reload config, and a real-time observability dashboard.
    3
    12
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sn391/kurd'

If you have feedback or need assistance with the MCP directory API, please join our Discord server