DEV Community

Cover image for Give Your Agent Dapr's Building Blocks Through the Dapr MCP Server
Casper Nielsen
Casper Nielsen

Posted on Edited on

Give Your Agent Dapr's Building Blocks Through the Dapr MCP Server

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Point Claude Code at it

claude mcp add --transport http dapr http://localhost:8080/
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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)