The Dapr MCP server turns a Dapr sidecar's building blocks into Model Context Protocol tools. It's alpha, so tool names and configuration can still change. What you get today is one MCP endpoint that lets any client – Claude Code, Cursor, VS Code, or an agent you wrote – save state, publish events, read secrets or take a distributed lock through the same sidecar your services use.
The server reads the sidecar's metadata and registers tools only for building blocks that have a component loaded. If there's no pub/sub component, there are no pub/sub tools. Three are always there: get_components, invoke_service and invoke_actor_method.
Set it up
Two in-memory components, in ./components:
# components/statestore.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.in-memory
version: v1
---
# components/pubsub.yaml
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.in-memory
version: v1
Install the server (or grab a binary from the repo's releases page) and run it next to a sidecar, over streamable HTTP on localhost:
go install github.com/dapr/dapr-mcp-server/cmd/dapr-mcp-server@latest
dapr init
dapr run --app-id dapr-mcp-server --resources-path ./components -- dapr-mcp-server --http localhost:8080
curl http://localhost:8080/readyz
Point Claude Code at it
claude mcp add --transport http dapr http://localhost:8080/
Call get_components first. It tells the client which state stores and brokers exist, and the other tools need those names.
Give a Python agent the same MCP tools
import asyncio
from dapr_agents import Agent
from dapr_agents.tool.mcp import MCPClient
async def main():
client = MCPClient(
allowed_tools={"get_components", "get_state", "save_state", "publish_event"}
)
await client.connect_streamable_http(server_name="dapr", url="http://localhost:8080/")
agent = Agent(
name="checklist-keeper",
role="Keeps a release checklist and announces changes",
instructions=[
"Call get_components first to find the state store and pub/sub names.",
"Keep the checklist under the key 'release-checklist'.",
"After every change, publish the new checklist to the topic 'checklist'.",
],
tools=client.get_all_tools(),
)
print(await agent.run("Add 'update the quickstart README' to the checklist."))
await client.close()
asyncio.run(main())
You'll need an API key for whichever LLM provider you've configured. allowed_tools keeps delete_state, execute_transaction and the rest out of the agent's reach. The docs say to treat destructive tools like those as needing human confirmation, and I'd start every agent on the smallest list that does the job.
Before it goes anywhere near a network
Authentication is off until you set AUTH_MODE (oidc, spiffe, dapr-sentry or hybrid). Without it, anyone who can reach the HTTP port can call every tool, so keep it on localhost until auth is on. Every loaded component turns into tools, so use component scopes to load only what the agent needs. Each tool also carries MCP safety annotations, which clients can use to tell reads from writes.
The Dapr MCP server section of the highlights post is here: https://www.diagrid.io/blog/dapr-1-19-release-highlights. It's alpha, so I want to hear what breaks. Point it at your own components and lmk which tool you reached for first 👀
Top comments (0)