Skip to content

Commit 4a78d4a

Browse files
authored
feat: add Pulsar MCP resources (#99)
1 parent 54af49e commit 4a78d4a

9 files changed

Lines changed: 6382 additions & 29 deletions

File tree

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,8 @@ agents/
1313
.serena/
1414
.envrc
1515
scripts/update-sdk-apiserver.sh
16+
17+
# Local RALPH authoring loop artifacts
18+
ralph/
19+
scripts/ralph*
20+
scripts/__pycache__/ralph*.pyc

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The server currently negotiates MCP protocol versions `2025-11-25`, `2025-06-18`
2323
- Pulsar Admin operations (topics, namespaces, tenants, schemas, etc.)
2424
- Pulsar Client operations (producers, consumers)
2525
- Functions, Sources, and Sinks management
26+
- Read-only MCP resources for context, catalog, and bounded admin summaries
2627
- **Multiple Connection Options**:
2728
- Connect to StreamNative Cloud with service account authentication
2829
- Connect directly to external Apache Kafka clusters
@@ -258,8 +259,8 @@ The StreamNative MCP Server allows you to enable or disable specific groups of f
258259

259260
| Feature | Description | Docs |
260261
|--------------------------|--------------------------------------------------|------|
261-
| `all-pulsar` | Enables all Pulsar admin and client tools, without Apache Kafka and StreamNative Cloud tools | |
262-
| `pulsar-admin` | Pulsar administrative operations (all admin tools)| |
262+
| `all-pulsar` | Enables all Pulsar admin and client tools, without Apache Kafka and StreamNative Cloud tools | [pulsar_resources.md](docs/tools/pulsar_resources.md) |
263+
| `pulsar-admin` | Pulsar administrative operations (all admin tools)| [pulsar_resources.md](docs/tools/pulsar_resources.md) |
263264
| `pulsar-client` | Pulsar client operations (produce/consume) | [pulsar_client_consume.md](docs/tools/pulsar_client_consume.md), [pulsar_client_produce.md](docs/tools/pulsar_client_produce.md) |
264265
| `pulsar-admin-brokers` | Manage Pulsar brokers | [pulsar_admin_brokers.md](docs/tools/pulsar_admin_brokers.md) |
265266
| `pulsar-admin-brokers-status` | Check Pulsar broker or proxy status | [pulsar_admin_status.md](docs/tools/pulsar_admin_status.md) |
@@ -280,6 +281,8 @@ The StreamNative MCP Server allows you to enable or disable specific groups of f
280281
| `pulsar-admin-sources` | Manage Pulsar Sources | [pulsar_admin_sources.md](docs/tools/pulsar_admin_sources.md) |
281282
| `pulsar-admin-topic-policy` | Configure Pulsar topic policies | [pulsar_admin_topic_policy.md](docs/tools/pulsar_admin_topic_policy.md) |
282283

284+
Pulsar admin feature gates also register read-only MCP resources for the matching admin surface. These resources use `pulsar://...` URIs, return JSON snapshots, and stay separate from write-capable tools; see [pulsar_resources.md](docs/tools/pulsar_resources.md) for the supported URI templates and safety boundaries.
285+
283286
---
284287

285288
#### StreamNative Cloud Features

docs/tools/pulsar_resources.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Pulsar Resources
2+
3+
The Pulsar resource surface exposes read-only MCP resources for lightweight cluster context, discovery, and bounded admin summaries. It is separate from Pulsar tools: tools are command-oriented operations, while resources only read current state and return JSON snapshots.
4+
5+
## Registration and discovery
6+
7+
Pulsar resources are registered next to the existing Pulsar tool wiring and use the same Pulsar admin feature gates as the matching tool families. No Pulsar resources are registered for unrelated features or for `pulsar-client` alone.
8+
9+
`pulsar://context` and `pulsar://resources` are registered only when at least one Pulsar admin resource family is enabled. `pulsar://resources` returns the catalog of resource URIs and URI templates actually registered for the active feature set.
10+
11+
All resource reads except `pulsar://resources` require a Pulsar session in the request context. Missing sessions return a clear error instead of falling back to environment state.
12+
13+
## Static resources
14+
15+
- `pulsar://context`: current Pulsar session connection metadata with authentication material redacted.
16+
- `pulsar://resources`: catalog of the registered Pulsar resource URIs and URI templates.
17+
- `pulsar://admin/v2/tenants`: tenant names known to the current Pulsar admin endpoint.
18+
- `pulsar://admin/v2/resource-quotas`: default resource quota for new namespace bundles.
19+
- `pulsar://admin/v2/status`: broker or proxy status for the current Pulsar admin endpoint.
20+
- `pulsar://admin/v2/clusters`: cluster names known to the current Pulsar admin endpoint.
21+
- `pulsar://admin/v2/broker-stats/summary`: bounded summary of broker monitoring metrics and load report.
22+
- `pulsar://admin/v2/worker/cluster`: bounded summary of Pulsar Functions workers.
23+
- `pulsar://admin/v2/worker/cluster/leader`: current Pulsar Functions worker leader.
24+
- `pulsar://admin/v2/worker/assignments`: bounded summary of Pulsar Functions worker assignments.
25+
- `pulsar://admin/v2/worker-stats/functionsmetrics`: bounded function instance stats reported by the Pulsar Functions worker.
26+
- `pulsar://admin/v2/worker-stats/metrics`: bounded summary of Pulsar Functions worker monitoring metrics.
27+
28+
All static resources return `application/json`.
29+
30+
## Resource templates
31+
32+
- `pulsar://admin/v2/tenants/{tenant}`: gets tenant configuration.
33+
- `pulsar://admin/v2/tenants/{tenant}/namespaces`: lists namespaces for a tenant.
34+
- `pulsar://admin/v2/namespaces/{tenant}/{namespace}`: gets namespace policies.
35+
- `pulsar://admin/v2/namespaces/{tenant}/{namespace}/topics`: lists topics for a namespace.
36+
- `pulsar://admin/v2/resource-quotas/{tenant}/{namespace}/{bundle}`: gets resource quota for a namespace bundle.
37+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/metadata`: gets parsed topic identity and sanitized topic properties.
38+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/stats`: gets a bounded topic statistics summary without publisher or consumer details.
39+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/partitions`: gets topic partition metadata.
40+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/policies/{policy}`: gets one read-only topic policy value. Supported policies are `retention`, `message-ttl`, `max-producers`, `max-consumers`, `max-unacked-messages-per-consumer`, `max-unacked-messages-per-subscription`, `persistence`, `delayed-delivery`, `dispatch-rate`, `subscription-dispatch-rate`, `deduplication`, `backlog-quotas`, `compaction-threshold`, `publish-rate`, and `inactive-topic-policies`.
41+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/schema`: gets the latest topic schema and version.
42+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/schema/{version}`: gets a specific topic schema version.
43+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/subscriptions`: lists subscriptions for a topic.
44+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/subscriptions/{subscription}/stats`: gets bounded subscription statistics without consumer details.
45+
- `pulsar://admin/v2/{domain}/{tenant}/{namespace}/{topic}/subscriptions/{subscription}/backlog`: gets subscription backlog counters without changing cursor state.
46+
- `pulsar://admin/v2/persistent/{tenant}/{namespace}/{topic}/subscriptions/{subscription}/cursor`: gets persistent topic cursor positions for a subscription.
47+
- `pulsar://admin/v3/functions/{tenant}/{namespace}`: lists Pulsar Functions for a namespace, bounded to the first 50 names.
48+
- `pulsar://admin/v3/functions/{tenant}/{namespace}/{function}/metadata`: gets sanitized Pulsar Function metadata.
49+
- `pulsar://admin/v3/functions/{tenant}/{namespace}/{function}/status`: gets bounded Pulsar Function runtime status without exception detail strings.
50+
- `pulsar://admin/v3/functions/{tenant}/{namespace}/{function}/stats`: gets bounded Pulsar Function statistics and user metric names.
51+
- `pulsar://admin/v3/sources/{tenant}/{namespace}`: lists Pulsar Sources for a namespace, bounded to the first 50 names.
52+
- `pulsar://admin/v3/sources/{tenant}/{namespace}/{source}/metadata`: gets sanitized Pulsar Source metadata.
53+
- `pulsar://admin/v3/sources/{tenant}/{namespace}/{source}/status`: gets bounded Pulsar Source runtime status without exception detail strings.
54+
- `pulsar://admin/v3/sinks/{tenant}/{namespace}`: lists Pulsar Sinks for a namespace, bounded to the first 50 names.
55+
- `pulsar://admin/v3/sinks/{tenant}/{namespace}/{sink}/metadata`: gets sanitized Pulsar Sink metadata.
56+
- `pulsar://admin/v3/sinks/{tenant}/{namespace}/{sink}/status`: gets bounded Pulsar Sink runtime status without exception detail strings.
57+
- `pulsar://admin/v3/packages/{type}/{tenant}/{namespace}`: lists packages by type and namespace, bounded to the first 50 names. Supported package types are `function`, `source`, and `sink`.
58+
- `pulsar://admin/v3/packages/{type}/{tenant}/{namespace}/{package}/versions`: lists package versions, bounded to the first 50 names.
59+
- `pulsar://admin/v3/packages/{type}/{tenant}/{namespace}/{package}/{version}/metadata`: gets sanitized metadata for one package version.
60+
- `pulsar://admin/v2/clusters/{cluster}`: sanitized configuration for a cluster.
61+
- `pulsar://admin/v2/brokers/{cluster}`: lists active brokers for a cluster.
62+
- `pulsar://admin/v2/clusters/{cluster}/failureDomains`: lists failure domains for a cluster.
63+
- `pulsar://admin/v2/clusters/{cluster}/failureDomains/{domain}`: gets a failure domain.
64+
- `pulsar://admin/v2/clusters/{cluster}/namespaceIsolationPolicies`: lists namespace isolation policies for a cluster.
65+
- `pulsar://admin/v2/clusters/{cluster}/namespaceIsolationPolicies/{policy}`: gets a namespace isolation policy.
66+
67+
Template reads return `application/json`. Topic templates accept `domain` values of `persistent` or `non-persistent`; `topic` is the local topic name path segment. Subscription cursor resources are persistent-only because they are backed by topic internal stats. Workload and package templates use Pulsar admin v3 APIs; functions worker resources use the current Pulsar admin v2 worker endpoints.
68+
69+
## Feature gates
70+
71+
The following feature gates register Pulsar resources. Each listed family is also enabled by `pulsar-admin`, `all-pulsar`, or `all`.
72+
73+
| Feature gate | Resource surface |
74+
|--------------|------------------|
75+
| `pulsar-admin-tenants` | tenant collection and tenant configuration resources |
76+
| `pulsar-admin-namespaces` | namespace collection by tenant |
77+
| `pulsar-admin-namespace-policy` | namespace policy resource |
78+
| `pulsar-admin-topics` | namespace topic collection, topic metadata, topic stats summary, and partition metadata resources |
79+
| `pulsar-admin-topic-policy` | read-only topic policy resource |
80+
| `pulsar-admin-schemas` | latest schema and schema version resources |
81+
| `pulsar-admin-subscriptions` | subscription collection, bounded subscription stats, backlog summary, and persistent cursor summary resources |
82+
| `pulsar-admin-resource-quotas` | default resource quota and namespace bundle resource quota resources |
83+
| `pulsar-admin-brokers-status` | broker or proxy status resource |
84+
| `pulsar-admin-clusters` | cluster collection, cluster configuration, failure-domain collection, and failure-domain resources |
85+
| `pulsar-admin-brokers` | broker collection by cluster |
86+
| `pulsar-admin-broker-stats` | broker stats summary resource |
87+
| `pulsar-admin-ns-isolation-policy` | namespace isolation policy collection and policy resources |
88+
| `pulsar-admin-functions` | function collection, metadata, status, and stats resources |
89+
| `pulsar-admin-sources` | source collection, metadata, and status resources |
90+
| `pulsar-admin-sinks` | sink collection, metadata, and status resources |
91+
| `pulsar-admin-packages` | package collection, package version collection, and package metadata resources |
92+
| `pulsar-admin-functions-worker` | functions worker cluster, leader, assignments, function stats, and metrics resources |
93+
94+
## Safety
95+
96+
Resource handlers are read-only regardless of whether write-capable Pulsar tools are enabled. They do not consume messages, commit cursors, clear backlog, unload topics, split bundles, delete resources, start workloads, or stop workloads. They also do not return tokens, auth params, key files, TLS private keys, or secret values.

pkg/cmd/mcp/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func newMcpServer(_ context.Context, configOpts *ServerOptions, logrusLogger *lo
114114
}
115115
}
116116

117+
mcp.PulsarAddResources(s, configOpts.Features)
117118
mcp.PulsarAdminAddBrokersTools(s, configOpts.ReadOnly, configOpts.Features)
118119
mcp.PulsarAdminAddStatusTools(s, configOpts.ReadOnly, configOpts.Features)
119120
mcp.PulsarAdminAddBrokerStatsTools(s, configOpts.ReadOnly, configOpts.Features)

pkg/mcp/builders/pulsar/status.go

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,8 @@ package pulsar
1717
import (
1818
"context"
1919
"fmt"
20-
"net/http"
2120
"strings"
2221

23-
"github.com/apache/pulsar-client-go/pulsaradmin/pkg/admin"
24-
pulsaradminauth "github.com/apache/pulsar-client-go/pulsaradmin/pkg/admin/auth"
25-
pulsaradminconfig "github.com/apache/pulsar-client-go/pulsaradmin/pkg/admin/config"
26-
"github.com/apache/pulsar-client-go/pulsaradmin/pkg/rest"
2722
"github.com/mark3labs/mcp-go/mcp"
2823
"github.com/mark3labs/mcp-go/server"
2924
"github.com/streamnative/streamnative-mcp-server/pkg/mcp/builders"
@@ -90,36 +85,17 @@ func (b *PulsarAdminStatusToolBuilder) buildStatusHandler() func(context.Context
9085
return mcp.NewToolResultError("Pulsar session not found in context"), nil
9186
}
9287

93-
cfg, err := session.GetPulsarCtlConfig()
88+
statusClient, err := session.GetAdminStatusClient()
9489
if err != nil {
95-
return b.handleError("get Pulsar configuration", err), nil
96-
}
97-
98-
authProvider, err := pulsaradminauth.GetAuthProvider((*pulsaradminconfig.Config)(cfg))
99-
if err != nil {
100-
return b.handleError("build status auth provider", err), nil
101-
}
102-
103-
statusClient := &rest.Client{
104-
ServiceURL: cfg.WebServiceURL,
105-
VersionInfo: admin.ReleaseVersion,
106-
HTTPClient: &http.Client{
107-
Timeout: admin.DefaultHTTPTimeOutDuration,
108-
Transport: authProvider,
109-
},
90+
return b.handleError("get Pulsar status client", err), nil
11091
}
11192

11293
data, err := statusClient.GetWithQueryParams("/status.html", nil, nil, false)
11394
if err != nil {
11495
return b.handleError("check Pulsar status", err), nil
11596
}
11697

117-
status := strings.TrimSpace(string(data))
118-
if status == "" {
119-
status = string(data)
120-
}
121-
122-
return mcp.NewToolResultText(status), nil
98+
return mcp.NewToolResultText(strings.TrimSpace(string(data))), nil
12399
}
124100
}
125101

0 commit comments

Comments
 (0)