Service Discovery for the Agentic World
Build, push, pull and resolve Agents, Tools and Skills -- like Docker, but for capabilities.
GitHub |
pip install capmesh |
Apache 2.0
Every agentic application today hardcodes which tools, agents, and services it calls. When a tool changes -- or when you need to swap GitHub for GitLab, or Snyk for Semgrep -- you hunt through code, update imports, rewrite tests, and redeploy. With a dozen agents, this is a crisis every quarter.
CapMesh solves this the way DNS solved IP address management: name what you need, not where it lives. Consumers ask for a capability ("security scan"). CapMesh returns the best available provider, enforcing governance, versioning, and environment isolation along the way. The consumer calls the provider directly using the returned binding. No proxy, no overhead.
from tools.github import GitHubReader from tools.snyk import SnykScanner from agents.crewai import SecurityAgent # Every dependency is hardcoded. # Change Snyk to Semgrep? Rewrite + redeploy. # Swap GitHub to GitLab? Rewrite + redeploy. # Restrict an intern? Add middleware. # Audit tool usage? Not possible. repo = GitHubReader(token=SECRET) scanner = SnykScanner(api_key=OTHER_SECRET) agent = SecurityAgent(endpoint=HARDCODED_URL)
import capmesh
mesh = capmesh.connect()
# Consumers ask for WHAT, not WHO.
repo = mesh.need("read a repository")
scanner = mesh.need("security vulnerability scan")
agent = mesh.need("security code review", kind="agent")
# Swap provider? One registry command.
# Restrict access? One YAML field.
# Audit trail? Automatic.
import capmesh
mesh = capmesh.connect()
# Any type -- natural language
result = mesh.need("security scan")
# Only agents
agent = mesh.need("security scan", kind="agent")
# Only MCP tools
tool = mesh.need("read repo", kind="tool", protocol="mcp")
| Change | Without CapMesh | With CapMesh |
|---|---|---|
| Swap GitHub for GitLab | 2-3 days: code + tests + deploy | 30 seconds: register new provider |
| Upgrade Snyk to Semgrep | 1-2 days | 30 seconds |
| Restrict intern to staging only | Days: middleware + ACLs | One YAML field: environment: [staging] |
| Audit which tool was used | Not possible without custom logging | Automatic: every resolution is traced |
| Add a new capability at runtime | Impossible without restart + redeploy | Register + discoverable instantly |
| Pin a team to scanner v2.x only | Manual version gates in each repo | version_constraint: ">=2.0,<3.0" |
It's not just about swapping one tool. It's what happens at scale:
Your company has 15 agents across 4 teams. Each agent hardcodes its tools. Now:
This isn't hypothetical. It's happening in every org building with agents today.
Agents bind to tools at resolution time, not at build time. Swap providers without touching agent code. Ever.
Register a new agent at 2pm. Every orchestrator discovers it at 2:01pm. No restart. No redeploy. No config change.
mesh.need("scan for security issues") — agents describe what they need in English. CapMesh finds who provides it.
LangGraph, CrewAI, Strands, AutoGen, Google ADK — all in one registry. Framework is metadata, not lock-in.
kind="agent", protocol="mcp" — ask for exactly what you need. An MCP tool. An A2A agent. A Skill. Or search all.
Skills declare what they need, not which tool. CapMesh resolves the tools independently. Same skill works with any vendor.
Visibility (public/org/private), environment isolation (staging vs prod), approval workflows — all in the manifest YAML.
Once published, a version can never be silently modified. sha256 digest verification. Tamper-proof by design.
Every resolution traced: who asked, what was available, what was selected, why others were rejected. Compliance-ready.
mesh.need("scan", version=">=2.0,<3.0") — compliance says "only scanner v2 in prod"? One parameter. Deterministic.
Deprecate a provider — agents auto-fallback to the next best version. No emergency patches. No breaking changes.
Resolution cache with auto-invalidation. First call: 16ms. Repeated calls: 0ms. Cache clears automatically when registry changes.
CapMesh is to agents what DNS is to IP addresses.
Before DNS: every application hardcoded IP addresses. Change a server? Update every client.
After DNS: applications use domain names. Move a server? Update one DNS record.
Before CapMesh: every agent hardcodes tool endpoints. Swap a tool? Update every agent.
After CapMesh: agents ask for capabilities. Swap a tool? Register one new provider.
| Role | Without CapMesh | With CapMesh |
|---|---|---|
| Agent developers | Import 5 tool SDKs, manage versions, handle auth per tool | mesh.need("what I need") — one line, any tool |
| Platform teams | Coordinate tool changes across 15 agent repos | Register once in CapMesh. All agents discover automatically. |
| Security teams | No visibility into which agents use which tools | Full audit trail. Approval workflows. Environment isolation. |
| Compliance | Manual tracking of tool usage | Every resolution traced. Version pinning. Immutable digests. |
| Leadership | "How many agents do we have? What can they do?" | capmesh search "*" — complete capability catalog |
Build, push, pull, tag, inspect. Manages the lifecycle of providers as versioned, immutable artifacts with sha256 content digests. Same mental model as Docker images -- but for agents, tools, and skills.
Resolve, discover, bind. Maps a capability name to the best available provider through a 9-step deterministic pipeline: find candidates, apply policy, filter by version constraint, select highest semver, build binding, record trace.
| Type | Protocol | Runtime Endpoint | Example |
|---|---|---|---|
| MCP Tool MCP | MCP (JSON-RPC) | Server name registered in the MCP client (e.g., github-mcp) |
GitHub MCP server, filesystem-mcp |
| A2A Agent A2A | A2A (HTTP) | Full HTTPS URL the agent listens on (e.g., https://security-agent.acme.com) |
LangGraph reviewer, CrewAI analyst |
| REST Tool REST | HTTP/REST | Full API base URL (e.g., https://api.sonarcloud.io/v1) |
SonarCloud, Datadog, Twilio |
| Skill SKILL | Skill (SKILL.md) | Path to the SKILL.md file; CapMesh resolves tool deps separately | Security review procedure, triage runbook |
CapMesh borrows Docker's mental model for the artifact plane. Every Docker workflow you know maps directly to a CapMesh command.
Validate a manifest and compute its content digest. Does not register it yet -- just proves the manifest is well-formed.
docker build -t myapp:1.0.0 .
capmesh tool build --directory ./github-reader/ capmesh agent build --directory ./security-agent/ capmesh skill build --directory ./code-review-skill/
Flags:
| Flag | Description | Default |
|---|---|---|
--directory, -d | Directory containing manifest.yaml | . |
--file, -f | Explicit path to manifest.yaml | -- |
--strict | Fail on any warning | false |
Example output -- building an MCP tool:
Register (push) a validated provider into the CapMesh registry. Equivalent to pushing an image to Docker Hub.
docker push myrepo/myapp:1.0.0
capmesh tool push --file ./github-reader/manifest.yaml capmesh agent push --file ./security-agent/manifest.yaml
Flags:
| Flag | Description | Default |
|---|---|---|
--file, -f | Path to manifest.yaml | required |
--registry, -r | CapMesh server URL | http://localhost:8080 |
--overwrite | Replace an existing draft (not allowed for approved versions) | false |
status: approved, its content is locked to that digest. Attempting to push the same version number with different content returns an error. This is the same guarantee as Docker content-addressable layers.
Download a provider manifest from the registry to local disk -- useful for inspection, forking, or offline use.
docker pull myrepo/myapp:1.0.0
capmesh tool pull tools/github-reader:2.3.1 capmesh agent pull security/crewai-reviewer:3.1.0
List all registered providers, optionally filtering by capability, type, or status.
docker images docker search ubuntu
capmesh search capmesh search --capability security.code.review capmesh search --type agent capmesh search --query "security scan"
Flags:
| Flag | Description |
|---|---|
--capability | Filter by exact capability ID |
--type | Filter by kind: tool | agent | skill |
--query | Natural language keyword search |
--status | Filter by governance status (approved | deprecated | revoked) |
--namespace | Filter to one namespace |
--json | Output as JSON array |
Print full details of a registered provider, including governance, interface, and all capabilities.
docker inspect myrepo/myapp:1.0.0
capmesh tool inspect tools/github-reader:2.3.1 capmesh agent inspect security/crewai-reviewer:3.1.0
Create an alias pointing an existing provider to an additional name or version label.
docker tag myapp:1.0.0 myapp:latest docker tag myapp:1.0.0 myrepo/myapp:stable
capmesh tag security/crewai-reviewer:3.1.0 \
security/crewai-reviewer:stable
capmesh tag tools/github-reader:2.3.1 \
tools/github-reader:latest
Authenticate to a CapMesh registry. Credentials are stored in ~/.capmesh/config.json.
docker login registry.example.com
capmesh login http://capmesh.acme.com:8080 capmesh login --token $CAPMESH_TOKEN http://capmesh.acme.com:8080
Start the CapMesh registry server. Equivalent to running the registry container.
docker run -d \ -p 5000:5000 \ -v registry-data:/var/lib/registry \ registry:2
capmesh server start --port 8080 \ --db /data/capmesh.db \ --host 0.0.0.0
Flags:
| Flag | Description | Default |
|---|---|---|
--port | Port to listen on | 8080 |
--host | Bind address | 127.0.0.1 |
--db | Path to SQLite database | ./capmesh.db |
--log-level | debug | info | warn | error | info |
--cors | Allowed CORS origins (comma-separated) | disabled |
Resolve a capability to its best provider right now, showing the full resolution trace.
capmesh resolve <capability> [--contract v1] [--caller identity] [--env production] [--constraint ">=2.0,<3.0"] [--kind agent|tool|skill] [--protocol mcp|a2a|rest|skill] [-k] [-p] [--trace] [--json]
Examples:
capmesh resolve "security scan" capmesh resolve "security scan" --kind agent capmesh resolve "read a repo" --protocol mcp capmesh resolve security.code.review -k agent -p a2a capmesh resolve security.code.review --trace --json
Flags:
| Flag | Short | Description |
|---|---|---|
--kind | -k | Filter by provider kind: agent | tool | skill |
--protocol | -p | Filter by protocol: mcp | a2a | rest | skill |
--trace | Show full 9-step resolution trace | |
--json | Output result as JSON | |
--contract | Require a specific contract version (default: v1) | |
--constraint | Semver range filter, e.g. ">=2.0,<3.0" | |
--caller | Caller identity for policy evaluation | |
--env | Caller environment for policy evaluation |
List all providers for a specific capability, showing what would win resolution.
capmesh providers <capability> [--contract v1] [--verbose]
Print the dependency graph for a provider -- what it requires and what provides those requirements.
capmesh graph <namespace/name:version>
An MCP tool wraps an existing MCP server. You do NOT rewrite the server. You write a manifest.yaml that tells CapMesh what capabilities this server provides and where to find it.
Scenario: The platform team has a github-mcp MCP server running. They want to register it so any agent can discover and use it.
# ./github-reader/manifest.yaml
metadata:
api_version: capmesh.io/v1alpha1
kind: tool
name: github-reader
namespace: tools
owner: platform-engineering
version: "2.3.1"
governance:
status: approved
visibility: public
environment: [production, staging]
interface:
protocol: mcp
server: github-mcp # the MCP server name known to the MCP client
tools:
- read_file
- search_code
- list_files
provides:
- capability: repository.read
contract: v1
description: Read files and directories from a GitHub repository
- capability: repository.search
contract: v1
description: Search code within a GitHub repository
Build and push:
An A2A agent exposes an HTTP endpoint that implements the A2A protocol. You declare the framework label so consumers know what kind of agent to expect, but CapMesh is framework-agnostic -- it just stores the label as metadata.
Scenario: The security team has a CrewAI-based security reviewer deployed at https://crewai-security.acme.com. They register it so any pipeline can discover it.
# ./security-agent/manifest.yaml
metadata:
api_version: capmesh.io/v1alpha1
kind: agent
name: crewai-security-reviewer
namespace: security
owner: security-engineering
version: "3.1.0"
labels:
framework: crewai # metadata only -- not enforced by CapMesh
governance:
status: approved
visibility: public
environment: [production, staging]
interface:
protocol: a2a
endpoint: https://crewai-security.acme.com # where the agent is ACTUALLY running
provides:
- capability: security.code.review
contract: v1
description: >
Review source code for security vulnerabilities (OWASP Top 10,
secrets exposure, dependency risks). Returns structured findings.
requires:
- capability: repository.read
contract: v1
- capability: repository.search
contract: v1
A Skill is a reusable procedure defined in a SKILL.md file. It contains human-readable (and LLM-readable) instructions. CapMesh resolves its tool dependencies separately at bind time, so the same skill works with GitHub, GitLab, or any other tool that satisfies the required capabilities.
Scenario: The engineering team writes a standard PR review procedure as a skill.
# ./pr-review-skill/manifest.yaml
metadata:
api_version: capmesh.io/v1alpha1
kind: skill
name: pr-review
namespace: engineering
owner: platform-engineering
version: "1.0.0"
governance:
status: approved
visibility: public
environment: [production, staging, dev]
interface:
protocol: skill
skill_file: SKILL.md # path relative to this manifest.yaml
provides:
- capability: pr.review
contract: v1
description: >
Perform a full pull request review: read changed files,
run a security scan, check coding standards, summarize findings.
requires:
- capability: repository.read
contract: v1
- capability: security.code.review
contract: v1
- capability: notification.send
contract: v1
# ./pr-review-skill/SKILL.md
# PR Review Skill
## Purpose
Perform a structured pull request review using the tools provided by the caller's environment.
## Steps
1. **Read the diff**
Use the `repository.read` tool to fetch the list of changed files in the PR.
For each file, read its content and the diff.
2. **Security scan**
Pass the changed file contents to the `security.code.review` tool.
Collect findings: severity (critical/high/medium/low), file, line, description.
3. **Summarize**
Produce a JSON report:
```json
{
"pr": "<PR identifier>",
"files": "<count>",
"findings": [ {"severity": "...", "file": "...", "line": "...", "desc": "..."} ],
"verdict": "approve | request_changes | needs_discussion"
}
```
4. **Notify**
Use the `notification.send` tool to post the summary to the PR thread.
## Notes
- Do NOT hardcode which tool provides repository.read or security.code.review.
Use whatever was bound by CapMesh at resolve time.
- If security.code.review returns 0 critical findings, verdict = approve.
- If any critical finding, verdict = request_changes.
A REST tool is any HTTP API that a consumer can call directly. CapMesh registers the base URL and any auth requirements so consumers can discover and use it without knowing the exact address.
Scenario: The security team uses SonarCloud for static analysis. They register it as a REST tool.
# ./sonarcloud-scanner/manifest.yaml
metadata:
api_version: capmesh.io/v1alpha1
kind: tool
name: sonarcloud-scanner
namespace: security
owner: security-engineering
version: "1.4.0"
governance:
status: approved
visibility: organization
environment: [production, staging]
interface:
protocol: rest
endpoint: https://sonarcloud.io/api # the actual REST API base URL
auth:
type: bearer
env_var: SONARCLOUD_TOKEN # consumer must have this env var set
provides:
- capability: security.static.analysis
contract: v1
description: Static code analysis via SonarCloud. POST /measures/component.
- capability: security.vulnerability.report
contract: v1
description: Fetch vulnerability report for a component.
CapMesh is a registry, not a proxy. When a consumer resolves a capability, it gets back a binding -- connection information for the provider. The consumer then calls the provider directly. CapMesh is never in the data path.
The provider team runs the actual service. They just write a manifest.yaml to register it. CapMesh does not host it, proxy it, or relay traffic to it -- it simply tells consumers where to find it. This is exactly how DNS works.
A2A Agent: An HTTPS endpoint that the agent team deploys and operates. Implements the A2A protocol (POST task, GET status, streaming). CapMesh stores this URL and returns it in the binding. The consumer calls this URL directly using an A2A client.
Example: https://security-agent.acme.com -- the security team's server.
MCP Tool: An MCP server registered under a name in the MCP client configuration. CapMesh stores the server name. The consumer uses this server name when calling its MCP client library.
Example: github-mcp -- the platform team's MCP server, known to the MCP client.
REST Tool: A publicly reachable HTTP/HTTPS API base URL. CapMesh stores the URL and auth requirements. The consumer makes direct HTTP calls to this URL.
Example: https://api.example.com/v1/scan -- the vendor's API endpoint.
Skill: A SKILL.md file stored in the CapMesh registry. CapMesh returns the instructions AND resolves all tool dependencies separately. The consumer executes the skill using the resolved tool bindings.
Example: SKILL.md path in the registry -- the team's procedure document.
MCP Tool binding:
{
"provider": "tools/github-reader:2.3.1",
"protocol": "mcp",
"connection": {
"server": "github-mcp",
"tool_name": "read_file"
},
"trace_id": "res_1a2b3c4d5e6f"
}
A2A Agent binding:
{
"provider": "security/crewai-security-reviewer:3.1.0",
"protocol": "a2a",
"connection": {
"endpoint": "https://crewai-security.acme.com",
"framework": "crewai"
},
"trace_id": "res_8444e343826b"
}
REST Tool binding:
{
"provider": "security/sonarcloud-scanner:1.4.0",
"protocol": "rest",
"connection": {
"endpoint": "https://sonarcloud.io/api",
"auth": {
"type": "bearer",
"env_var": "SONARCLOUD_TOKEN"
}
},
"trace_id": "res_9c8b7a6d5e4f"
}
Skill binding (dual binding -- instructions + tool deps resolved separately):
{
"provider": "engineering/pr-review:1.0.0",
"protocol": "skill",
"connection": {
"instructions": "# PR Review Skill\n\n## Steps\n...",
"tool_bindings": [
{
"capability": "repository.read",
"provider": "tools/github-reader:2.3.1",
"protocol": "mcp",
"connection": { "server": "github-mcp", "tool_name": "read_file" }
},
{
"capability": "security.code.review",
"provider": "security/crewai-security-reviewer:3.1.0",
"protocol": "a2a",
"connection": { "endpoint": "https://crewai-security.acme.com" }
},
{
"capability": "notification.send",
"provider": "comms/slack-notifier:4.0.2",
"protocol": "mcp",
"connection": { "server": "slack-mcp", "tool_name": "post_message" }
}
]
},
"trace_id": "res_3d2c1b0a9e8f"
}
"""
Example: A CI pipeline that resolves a security scanner and runs it.
The pipeline does NOT know which scanner will be returned.
"""
import capmesh
# One-line setup
mesh = capmesh.connect()
# --- Resolve a capability (exact ID) ---
result = mesh.resolve("security.code.review")
print(f"Provider: {result.provider}") # security/crewai-security-reviewer:3.1.0
print(f"Protocol: {result.protocol}") # a2a
print(f"Trace ID: {result.trace_id}") # res_8444e343826b
print(f"Endpoint: {result.binding.connection['endpoint']}")
# https://crewai-security.acme.com
# --- Resolve a capability (natural language) ---
binding = mesh.need("read files from a code repository")
print(f"Provider: {binding.provider}") # tools/github-reader:2.3.1
print(f"Protocol: {binding.protocol}") # mcp
print(f"Server: {binding.connection['server']}") # github-mcp
# --- Filter by kind and/or protocol ---
agent = mesh.need("security scan", kind="agent")
tool = mesh.need("read repo", kind="tool", protocol="mcp")
print(f"Agent: {agent.provider}") # security/crewai-security-reviewer:3.1.0
print(f"Tool: {tool.provider}") # tools/github-reader:2.3.1
# --- Exact ID with filter ---
result = mesh.resolve("security.code.review", kind="agent")
# --- List all providers for a capability ---
providers = mesh.providers("security.code.review")
for p in providers:
print(f" {p.provider} ({p.protocol})")
No SDK required. Any HTTP client works.
POST http://capmesh:8080/v1/resolve
Content-Type: application/json
{
"capability": "security.code.review",
"contract": "v1",
"caller": {
"identity": "ci-pipeline",
"environment": "production",
"namespace": "eng"
}
}
Full response:
{
"status": "resolved",
"trace_id": "res_8444e343826b",
"capability": "security.code.review",
"contract": "v1",
"provider": "security/crewai-security-reviewer:3.1.0",
"protocol": "a2a",
"binding": {
"connection": {
"endpoint": "https://crewai-security.acme.com",
"framework": "crewai"
}
},
"resolution_ms": 14,
"selected_from": 3,
"candidates": [
{ "provider": "security/crewai-security-reviewer:3.1.0", "passed": true, "reason": "selected (highest semver)" },
{ "provider": "security/langgraph-security-reviewer:2.4.0", "passed": true, "reason": "available but lower semver" },
{ "provider": "security/code-review-skill:1.2.0", "passed": true, "reason": "available but lower semver" }
]
}
Once you have the binding, you call the provider directly. CapMesh is no longer involved.
import httpx
import json
from mcp import MCPClient
from a2a import A2AClient
def resolve_and_call(capability: str, payload: dict) -> dict:
"""
Resolve a capability and call the provider.
Works regardless of which provider is returned.
"""
# Step 1: resolve
resp = httpx.post("http://capmesh:8080/v1/resolve", json={
"capability": capability,
"contract": "v1",
"caller": {"identity": "my-agent", "environment": "production"}
})
resp.raise_for_status()
result = resp.json()
protocol = result["protocol"]
connection = result["binding"]["connection"]
# Step 2: call the provider directly
if protocol == "a2a":
client = A2AClient(base_url=connection["endpoint"])
return client.submit_task(payload)
elif protocol == "mcp":
client = MCPClient()
return client.call_tool(
server=connection["server"],
tool=connection.get("tool_name", "default"),
arguments=payload,
)
elif protocol == "rest":
token = os.environ.get(connection["auth"]["env_var"])
return httpx.post(
connection["endpoint"],
json=payload,
headers={"Authorization": f"Bearer {token}"},
).json()
elif protocol == "skill":
instructions = connection["instructions"]
tool_bindings = connection["tool_bindings"]
# Execute the skill instructions using the bound tools
return execute_skill(instructions, tool_bindings, payload)
else:
raise ValueError(f"Unknown protocol: {protocol}")
# Use it -- no hardcoded providers anywhere
findings = resolve_and_call("security.code.review", {
"repo": "acme-corp/backend-api",
"pr": "1247",
"branch": "feature/auth-refactor"
})
Every framework follows the same 3-step pattern. The only thing that changes is how you wrap the CapMesh binding into your framework's tool type.
mesh.need("what I need") -- discover the capabilityBefore using a capability, agents need to find it. Three ways:
import capmesh
mesh = capmesh.connect()
# Natural language -- agent describes what it needs
binding = mesh.need("read code from a repository")
binding = mesh.need("scan for security vulnerabilities")
binding = mesh.need("notify the team on slack")
# With kind/protocol filters
agent = mesh.need("security scan", kind="agent")
tool = mesh.need("read a repo", kind="tool", protocol="mcp")
# Exact ID -- when you know the capability name
binding = mesh.resolve("security.code.review")
binding = mesh.resolve("security.code.review", kind="agent")
# Explore -- see what's available (ranked by relevance)
results = mesh.discover("security")
# [
# {capability: "security.code.review", score: 0.9, reason: "substring match"},
# {capability: "security.scan", score: 0.9, reason: "substring match"},
# ]
# List providers for a capability
providers = mesh.providers("security.code.review")
# Register a manifest
mesh.register("manifest.yaml")
# Search the registry
results = mesh.search("security")
Every resolution returns a binding with protocol-specific connection info:
| Protocol | What you get back | How you call it |
|---|---|---|
mcp | {"server": "github-mcp", "tool_name": "read_file"} | MCP client calls the server |
a2a | {"endpoint": "https://agent.example.com"} | A2A client calls the endpoint |
rest | {"endpoint": "https://api.example.com", "auth_type": "bearer"} | HTTP POST to the endpoint |
skill | {"instructions": "SKILL.md", "tool_bindings": [...]} | Load instructions + use resolved tools |
# pip install crewai capmesh
from crewai import Agent, Task, Crew
import capmesh
# Connect to CapMesh once
mesh = capmesh.connect()
# Discover capabilities (optionally filter by kind/protocol)
repo = mesh.need("read a repository", kind="tool", protocol="mcp")
scan = mesh.need("security scan", kind="agent")
# Build CrewAI tools from CapMesh bindings
def make_crewai_tool(binding):
if binding.binding.protocol == "mcp":
# from crewai_tools import MCPTool
# return MCPTool(server=binding.binding.connection["server"])
return f"MCPTool(server='{binding.binding.connection['server']}')"
elif binding.binding.protocol == "a2a":
# return A2ATool(endpoint=binding.binding.connection["endpoint"])
return f"A2ATool(endpoint='{binding.binding.connection['endpoint']}')"
repo_tool = make_crewai_tool(repo) # MCPTool resolved dynamically
scan_tool = make_crewai_tool(scan) # A2ATool resolved dynamically
# Create CrewAI agent with dynamic tools
# agent = Agent(
# role="Security Reviewer",
# goal="Find vulnerabilities in code",
# tools=[repo_tool, scan_tool], # DYNAMIC -- not hardcoded
# )
# task = Task(description="Review PR #42", agent=agent)
# crew = Crew(agents=[agent], tasks=[task])
# result = crew.kickoff()
# pip install langgraph langchain capmesh
from langchain_core.tools import tool
from langgraph.graph import StateGraph
import capmesh
mesh = capmesh.connect()
# Resolve tools at graph build time (filter by kind/protocol as needed)
repo_binding = mesh.need("read repository code", kind="tool", protocol="mcp")
scan_binding = mesh.need("security review", kind="agent")
# Create LangChain tools from bindings
@tool
def read_repo(repo: str) -> dict:
"""Read repository files. Provider resolved by CapMesh."""
conn = repo_binding.binding.connection
if repo_binding.binding.protocol == "mcp":
return mcp_client.call(conn["server"], {"repo": repo})
return httpx.get(conn["endpoint"], params={"repo": repo}).json()
@tool
def security_scan(files: list) -> dict:
"""Run security scan. Provider resolved by CapMesh."""
conn = scan_binding.binding.connection
return a2a_client.submit_task(conn["endpoint"], {"files": files})
# Build LangGraph
# graph = StateGraph(ReviewState)
# graph.add_node("read", read_repo)
# graph.add_node("scan", security_scan)
# graph.add_edge("read", "scan")
# app = graph.compile()
# result = app.invoke({"repo": "myorg/webapp"})
# pip install strands-agents capmesh
import capmesh
mesh = capmesh.connect()
# Discover what's available
results = mesh.discover("repository")
print(f"Found {len(results)} capabilities matching 'repository':")
for r in results:
print(f" [{r.score:.1f}] {r.capability} -- {r.reason}")
# Resolve the best one (filter to MCP tools only)
repo = mesh.need("read repository", kind="tool", protocol="mcp")
# Build Strands agent
# from strands import Agent
# from strands.tools import MCPTool, HTTPTool
#
# if repo.binding.protocol == "mcp":
# tool = MCPTool(repo.binding.connection["server"])
# elif repo.binding.protocol == "rest":
# tool = HTTPTool(repo.binding.connection["endpoint"])
#
# agent = Agent(tools=[tool])
# result = agent("Read the auth module from myorg/webapp")
# pip install autogen-agentchat capmesh
import capmesh
mesh = capmesh.connect()
# Resolve and create tool functions
def capmesh_tool(capability: str, kind: str = None, protocol: str = None):
result = mesh.need(capability, kind=kind, protocol=protocol)
conn = result.binding.connection
def tool_fn(**kwargs):
if result.binding.protocol == "a2a":
return a2a_client.call(conn["endpoint"], kwargs)
elif result.binding.protocol == "mcp":
return mcp_client.call(conn["server"], kwargs)
elif result.binding.protocol == "rest":
return httpx.post(conn["endpoint"], json=kwargs).json()
tool_fn.__name__ = capability.replace(".", "_")
return tool_fn
# Register tools with AutoGen (filter by kind/protocol to be explicit)
read_repo = capmesh_tool("repository.read", kind="tool", protocol="mcp")
scan_code = capmesh_tool("security.code.review", kind="agent")
# from autogen import AssistantAgent, UserProxyAgent
# assistant = AssistantAgent("reviewer", llm_config={...})
# assistant.register_function({"read_repo": read_repo, "scan_code": scan_code})
# user_proxy = UserProxyAgent("user")
# user_proxy.initiate_chat(assistant, message="Review PR #42")
# pip install google-adk capmesh
import capmesh
mesh = capmesh.connect()
# Resolve multiple capabilities at once (with kind/protocol filters)
bindings = {
"read a repo": mesh.need("read a repo", kind="tool", protocol="mcp"),
"security scan": mesh.need("security scan", kind="agent"),
"send notification": mesh.need("send notification", kind="tool"),
}
# Convert to Google ADK tools
# from google.adk import Agent
# from google.adk.tools import FunctionTool
#
# adk_tools = []
# for cap, binding in bindings.items():
# conn = binding.binding.connection
# if binding.binding.protocol == "mcp":
# adk_tools.append(FunctionTool(name=cap,
# fn=lambda **kw: mcp_client.call(conn["server"], kw)))
# elif binding.binding.protocol == "a2a":
# adk_tools.append(FunctionTool(name=cap,
# fn=lambda **kw: a2a_client.call(conn["endpoint"], kw)))
#
# agent = Agent(model="gemini-2.0-flash", tools=adk_tools)
# result = agent.run("Review this repository for security issues")
# pip install openai capmesh
import capmesh
mesh = capmesh.connect()
# Generate OpenAI function schemas from CapMesh bindings
def make_openai_tool(capability: str, kind: str = None, protocol: str = None):
result = mesh.need(capability, kind=kind, protocol=protocol)
return {
"type": "function",
"function": {
"name": capability.replace(".", "_").replace(" ", "_"),
"description": f"Resolved: {result.provider_name}:{result.provider_version} via {result.binding.protocol}",
"parameters": {"type": "object", "properties": {
"input": {"type": "string", "description": "Input data"}
}},
},
"_capmesh_binding": result.binding, # keep binding for dispatch
}
tools = [
make_openai_tool("repository.read", kind="tool", protocol="mcp"),
make_openai_tool("security.code.review", kind="agent"),
]
# from openai import OpenAI
# client = OpenAI()
# response = client.chat.completions.create(
# model="gpt-4",
# messages=[{"role": "user", "content": "Review PR #42"}],
# tools=tools,
# )
# # Dispatch tool calls to CapMesh-resolved providers
# for call in response.choices[0].message.tool_calls:
# binding = tools[call.function.name]._capmesh_binding
# result = dispatch(binding, call.function.arguments)
# pip install semantic-kernel capmesh
import capmesh
mesh = capmesh.connect()
# Resolve and create SK kernel functions (filter by kind/protocol)
repo = mesh.need("read a repository", kind="tool", protocol="mcp")
scan = mesh.need("security scan", kind="agent")
# import semantic_kernel as sk
# from semantic_kernel.functions import kernel_function
#
# kernel = sk.Kernel()
#
# @kernel_function(name="read_repo")
# def read_repo(repo_name: str) -> str:
# conn = repo.binding.connection
# return mcp_client.call(conn["server"], {"repo": repo_name})
#
# @kernel_function(name="security_scan")
# def security_scan(code: str) -> str:
# conn = scan.binding.connection
# return a2a_client.call(conn["endpoint"], {"code": code})
#
# kernel.add_function("tools", read_repo)
# kernel.add_function("tools", security_scan)
# result = await kernel.invoke("tools", "read_repo", repo_name="myorg/app")
No SDK required. Just one HTTP call.
# Python
import httpx
resp = httpx.post("http://capmesh:8080/v1/resolve", json={
"capability": "security.code.review", "contract": "v1",
"caller": {"identity": "my-app"}
})
binding = resp.json()
# Use binding["binding"]["endpoint"] to call the provider
# curl
curl -X POST http://capmesh:8080/v1/resolve \
-H "Content-Type: application/json" \
-d '{"capability":"security.code.review","contract":"v1","caller":{"identity":"my-app"}}'
// JavaScript
const resp = await fetch("http://capmesh:8080/v1/resolve", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({capability: "security.code.review", contract: "v1", caller: {identity: "my-app"}})
});
const binding = await resp.json();
// Use binding.binding.endpoint
// Go
body := `{"capability":"security.code.review","contract":"v1","caller":{"identity":"my-app"}}`
resp, _ := http.Post("http://capmesh:8080/v1/resolve", "application/json", strings.NewReader(body))
CapMesh caches resolution results with a 30-second TTL. Cache auto-invalidates when providers are registered, deleted, or deprecated.
import capmesh
mesh = capmesh.connect()
# First call: hits SQLite (~16ms)
mesh.need("security scan")
# Second call: cache hit (~0ms)
mesh.need("security scan")
# Register a new provider: cache auto-invalidates
mesh.register("new_scanner_manifest.yaml")
# Next call: fresh resolution (~16ms), picks new provider
mesh.need("security scan")
# Check cache performance
print(mesh.cache_stats)
# {"hits": 142, "misses": 18, "size": 12}
mesh.need(), get back a binding (protocol + connection info), wrap it into your framework's tool type. Optionally add kind= or protocol= to narrow results. The framework-specific code is 3-5 lines. Everything else -- discovery, versioning, policy, audit, caching -- is handled by CapMesh.
You have a LangGraph agent deployed at https://my-agent.acme.com. It has been running for months. You want to make it discoverable through CapMesh.
What you do: Write one manifest.yaml. Push it. Done.
# manifest.yaml -- the ONLY thing you need
metadata:
api_version: capmesh.io/v1alpha1
kind: agent
name: my-existing-langgraph-agent
namespace: ml
owner: ml-team
version: "1.0.0"
labels:
framework: langgraph # metadata only
governance:
status: approved
visibility: public
environment: [production]
interface:
protocol: a2a
endpoint: https://my-agent.acme.com # already running, no changes needed
provides:
- capability: data.analysis
contract: v1
description: Analyze datasets and produce statistical summaries
You have an MCP server named my-github-mcp in your MCP client config. It has been running for months. You want to make it discoverable through CapMesh.
# manifest.yaml
metadata:
api_version: capmesh.io/v1alpha1
kind: tool
name: my-github-mcp-tool
namespace: tools
owner: platform-eng
version: "3.0.0"
governance:
status: approved
visibility: public
environment: [production, staging]
interface:
protocol: mcp
server: my-github-mcp # the existing server name -- no changes needed
tools:
- read_file
- list_files
provides:
- capability: repository.read
contract: v1
description: Read files from a GitHub repository via MCP
| Scenario | What provider team does | What changes in provider code |
|---|---|---|
| Register existing A2A agent | Write manifest.yaml, run capmesh agent push | Nothing |
| Register existing MCP server | Write manifest.yaml, run capmesh tool push | Nothing |
| Register existing REST API | Write manifest.yaml, run capmesh tool push | Nothing |
| Write a new skill | Write manifest.yaml + SKILL.md, run capmesh skill push | Nothing (skill is docs, not code) |
| Upgrade to a new version | Bump version in manifest.yaml, push again | Nothing (if the endpoint didn't change) |
A CI pipeline resolves 4 capabilities, chains results, and produces a final report. The pipeline never hardcodes which tools to use.
"""pr_review_pipeline.py"""
import httpx, json
CAPMESH = "http://capmesh:8080"
PR = {"repo": "acme-corp/backend-api", "pr": "1247", "branch": "feature/auth-refactor"}
def resolve(capability):
r = httpx.post(f"{CAPMESH}/v1/resolve", json={
"capability": capability, "contract": "v1",
"caller": {"identity": "ci-pipeline", "environment": "production"}
})
r.raise_for_status()
return r.json()
# Step 1: Find the repo reader
repo_binding = resolve("repository.read")
# Step 2: Find a security reviewer
sec_binding = resolve("security.code.review")
# Step 3: Find a code quality checker
quality_binding = resolve("code.quality.check")
# Step 4: Find a notification channel
notify_binding = resolve("notification.send")
print(json.dumps({
"repository.read": repo_binding["provider"],
"security.review": sec_binding["provider"],
"quality.check": quality_binding["provider"],
"notification.send": notify_binding["provider"],
}, indent=2))
# Now call each provider using its binding...
# (abbreviated: see section 6c for full call logic)
The security team decides to replace Snyk with Semgrep. The pipeline code does not change. The swap takes 30 seconds.
Before:
The security team pushes a new version of Semgrep:
$ capmesh tool push --file ./semgrep-scanner/manifest.yaml # manifest.yaml has version: "3.0.0" -- higher than snyk:2.1.0
After: All pipelines that resolve security.static.analysis/v1 now get Semgrep 3.0.0 automatically. No pipeline code changed.
If the team needs to roll back, they deprecate Semgrep 3.0.0:
capmesh tool deprecate security/semgrep-scanner:3.0.0
A new team just deployed a specialized code reviewer for Python at https://python-reviewer.acme.com. They register it at runtime. No restarts needed anywhere.
import httpx, json
# Register via HTTP API -- no CLI needed
manifest = {
"metadata": {
"api_version": "capmesh.io/v1alpha1",
"kind": "agent",
"name": "python-security-reviewer",
"namespace": "security",
"owner": "python-guild",
"version": "1.0.0",
"labels": {"framework": "strands", "language": "python"}
},
"governance": {
"status": "approved",
"visibility": "organization",
"environment": ["production", "staging"]
},
"interface": {
"protocol": "a2a",
"endpoint": "https://python-reviewer.acme.com"
},
"provides": [
{
"capability": "security.code.review",
"contract": "v1",
"description": "Python-specialized security review: type confusion, injection, async pitfalls"
}
]
}
resp = httpx.post(
"http://capmesh:8080/v1/providers/register",
json=manifest,
headers={"Authorization": "Bearer $CAPMESH_TOKEN"}
)
print(resp.json())
status: approved. Dynamic loading WITH governance = control at scale.
Every provider has a status in its governance block. Only approved providers are resolvable.
governance: status: approved # approved | deprecated | revoked
| Status | Resolvable | Visible in search | Audit trail | Use case |
|---|---|---|---|---|
approved | Yes | Yes | Yes | Active providers in production |
deprecated | No | Yes | Yes | Phased out -- agents auto-fall-back to next best |
revoked | No | No | Yes | Security incident -- completely blocked |
Changing status via CLI:
governance: environment: [production] # staging callers CANNOT resolve this
A staging CI pipeline resolving database.write will NEVER get the production database provider. Automatic. No extra firewall rules, no middleware.
governance: visibility: private # only owner can resolve # visibility: organization # only callers in same org as owner # visibility: public # anyone
| Level | Who can resolve | Use case |
|---|---|---|
public | Any caller with valid environment | Shared platform tools |
organization | Callers in the same org as the owner | Team-internal tools |
private | Callers whose identity matches the owner | Security-sensitive scanners, licensed tools |
Consumers can require a specific semver range, regardless of what version exists at the top of the registry.
import capmesh
mesh = capmesh.connect()
# Exact ID resolve with kind filter
result = mesh.resolve("security.code.review", kind="agent")
# For version constraints, use the CLI or raw HTTP API:
# capmesh resolve security.code.review --constraint ">=2.0.0,<3.0.0" --kind agent
Once an approved version is pushed, its digest is locked. You cannot push different content under the same version number.
Every resolution is stored in SQLite with full candidate list.
GET http://capmesh:8080/v1/resolutions/res_8444e343826b
{
"trace_id": "res_8444e343826b",
"timestamp": "2026-09-23T14:32:15Z",
"caller": {
"identity": "ci-pipeline",
"environment": "production",
"namespace": "eng"
},
"requested_capability": "security.code.review",
"requested_contract": "v1",
"version_constraint": null,
"candidates_evaluated": [
{
"provider": "security/crewai-security-reviewer:3.1.0",
"passed": true,
"reason": "selected -- highest semver"
},
{
"provider": "security/langgraph-security-reviewer:2.4.0",
"passed": true,
"reason": "available -- lower semver"
},
{
"provider": "security/openai-reviewer:2.0.0",
"passed": false,
"reason": "governance.status=deprecated"
}
],
"selected_provider": "security/crewai-security-reviewer:3.1.0",
"protocol": "a2a",
"outcome": "success",
"resolution_ms": 16.0
}
Query all resolutions for a capability in the last hour:
GET http://capmesh:8080/v1/resolutions?capability=security.code.review&since=2026-09-23T13:00:00Z
All endpoints are on http://<capmesh-host>:8080. Responses are JSON. The server returns standard HTTP status codes.
| Method | Path | Description |
|---|---|---|
POST | /v1/providers/register | Register (push) a provider manifest |
GET | /v1/providers/{namespace}/{name} | List all versions of a provider |
GET | /v1/providers/{namespace}/{name}/{version} | Get a specific provider version |
DELETE | /v1/providers/{namespace}/{name}/{version} | Revoke a provider version |
PATCH | /v1/providers/{namespace}/{name}/{version}/status | Update governance status |
GET | /v1/capabilities/{id}/providers | List providers for a capability |
POST | /v1/search | Keyword / natural language search |
Request body: full manifest YAML/JSON as described in section 4.
POST /v1/providers/register
Content-Type: application/json
{ ...manifest object... }
# 201 Created
{
"status": "registered",
"provider": "security/crewai-security-reviewer:3.1.0",
"digest": "sha256:3f9f55a4...",
"capabilities_indexed": ["security.code.review/v1"],
"immediately_resolvable": true
}
# 409 Conflict (version exists with different digest)
{
"error": "version_conflict",
"message": "security/crewai-security-reviewer:3.1.0 already exists with digest sha256:3f9f55a4...",
"hint": "Bump the version number to publish new content"
}
GET /v1/capabilities/security.code.review/providers?contract=v1&status=approved
{
"capability": "security.code.review",
"contract": "v1",
"providers": [
{
"namespace": "security",
"name": "crewai-security-reviewer",
"version": "3.1.0",
"protocol": "a2a",
"status": "approved",
"digest": "sha256:3f9f55a4..."
},
{
"namespace": "security",
"name": "langgraph-security-reviewer",
"version": "2.4.0",
"protocol": "a2a",
"status": "approved",
"digest": "sha256:9b2c71e8..."
}
],
"total": 2
}
POST /v1/search
{
"query": "security scan",
"type": "tool",
"status": "approved",
"limit": 10
}
{
"results": [
{
"namespace": "security",
"name": "sonarcloud-scanner",
"version": "1.4.0",
"kind": "tool",
"protocol": "rest",
"capabilities": ["security.static.analysis/v1", "security.vulnerability.report/v1"],
"score": 0.91
}
],
"total": 1
}
| Method | Path | Description |
|---|---|---|
POST | /v1/resolve | Resolve a capability to its best provider |
POST | /v1/need | Natural language resolve ("notify the team") |
POST | /v1/discover | Ranked list of matches for exploration |
GET | /v1/resolutions/{trace_id} | Get full trace for one resolution |
GET | /v1/resolutions | Query resolution history (filter by capability, caller, time) |
POST /v1/resolve
{
"capability": "security.code.review",
"contract": "v1",
"version_constraint": ">=2.0.0",
"caller": {
"identity": "ci-pipeline",
"environment": "production",
"namespace": "eng"
}
}
# 200 OK
{
"status": "resolved",
"trace_id": "res_8444e343826b",
"capability": "security.code.review",
"contract": "v1",
"provider": "security/crewai-security-reviewer:3.1.0",
"protocol": "a2a",
"binding": {
"connection": {
"endpoint": "https://crewai-security.acme.com",
"framework": "crewai"
}
},
"resolution_ms": 14,
"selected_from": 3,
"candidates": [
{ "provider": "security/crewai-security-reviewer:3.1.0", "passed": true, "reason": "selected" },
{ "provider": "security/langgraph-security-reviewer:2.4.0", "passed": true, "reason": "lower semver" },
{ "provider": "security/code-review-skill:1.2.0", "passed": true, "reason": "lower semver" }
]
}
# 404 Not Found
{
"status": "failed",
"error": "no_provider",
"message": "no approved provider satisfies security.code.review/v1 for env=dev",
"trace_id": "res_0000000000001"
}
POST /v1/need
{
"query": "notify the team about a build failure",
"caller": {
"identity": "ci-pipeline",
"environment": "production"
}
}
{
"status": "resolved",
"matched_capability": "notification.send",
"match_score": 0.88,
"trace_id": "res_2e3f4a5b6c7d",
"provider": "comms/slack-notifier:4.0.2",
"protocol": "mcp",
"binding": {
"connection": {
"server": "slack-mcp",
"tool_name": "post_message"
}
},
"resolution_ms": 18
}
| Method | Path | Description |
|---|---|---|
POST | /v1/artifacts/publish | Publish a SKILL.md or other artifact |
GET | /v1/artifacts/{ns}/{name}/{version} | Retrieve an artifact (e.g., SKILL.md content) |
GET | /v1/artifacts/{ns}/{name}/{version}/digest | Get digest for verification |
| Method | Path | Description |
|---|---|---|
GET | /healthz | Health check (returns 200 OK with {"status":"ok"}) |
GET | /v1/stats | Registry statistics (provider count, resolution count) |
GET | /v1/capabilities | List all known capability IDs |
GET /healthz
{"status": "ok", "version": "0.9.0", "providers": 34, "resolutions_today": 1247}
GET /v1/stats
{
"providers": {
"total": 34,
"approved": 28,
"deprecated": 4,
"revoked": 2
},
"capabilities": {
"total_ids": 19
},
"resolutions": {
"total": 94321,
"today": 1247,
"avg_ms": 15.4,
"success_rate": 0.997
}
}
pip install capmesh pip install "capmesh[server]" # for the registry server component
capmesh server start --port 8080
# 1. Write a manifest.yaml (see section 4 for full examples) # 2. Build and push capmesh tool build --directory ./my-tool/ capmesh tool push --file ./my-tool/manifest.yaml # 3. Verify capmesh tool inspect tools/my-tool:1.0.0
capmesh resolve my.capability --caller test --env dev
git clone https://github.com/capmesh/capmesh.git cd capmesh pip install -e ".[server,dev]" python demo/registry/generate_providers.py # generates 34 sample providers python demo/app.py # runs the full orchestrator demo # Step-by-step demos python demo/01_build.py # validate manifests, show digests python demo/02_register.py # register all providers python demo/03_resolve.py # resolve capabilities, show trace python demo/04_benefits.py # WITH vs WITHOUT comparison python demo/05_production.py # production HTTP API usage python demo/06_governance.py # status, visibility, version pinning python demo/07_natural_language.py # mesh.need() demo
pytest tests/ -v # 164 tests pytest tests/ -v --cov=capmesh # with coverage (87%)