use-dedalus-mcp

star 1

Use when developers need to build MCP (Model Context Protocol) servers with dedalus_mcp—covers creating tools via @tool, exposing resources via @resource, defining user prompts via @prompt, using context utilities (progress, info, cancellation), and registering components modularly with collect().

adamsardo By adamsardo schedule Updated 1/24/2026

name: use-dedalus-mcp description: 'Use when developers need to build MCP (Model Context Protocol) servers with dedalus_mcp—covers creating tools via @tool, exposing resources via @resource, defining user prompts via @prompt, using context utilities (progress, info, cancellation), and registering components modularly with collect().'

Dedalus MCP Server

dedalus_mcp is a Python framework for building MCP (Model Context Protocol) servers. Build once, works with any MCP-compatible client.

Installation

pip install dedalus-mcp dedalus-labs

Quick Start

from dedalus_mcp import MCPServer, tool

@tool(description="Add two numbers")
def add(a: int, b: int) -> int:
    return a + b

server = MCPServer("calculator")
server.collect(add)

if __name__ == "__main__":
    import asyncio
    asyncio.run(server.serve())

Run with: python server.py → Serves at http://127.0.0.1:8000/mcp

Server Primitives

MCP servers expose three types of capabilities:

Primitive Control Description
Tools Model Functions the LLM calls during reasoning
Resources Model/User Data the LLM can read for context
Prompts User Message templates users select and render

Tools

Tools let agents call your Python functions. Decorate, register, serve.

Basic Tool

from dedalus_mcp import tool

@tool(description="Multiply two numbers")
def multiply(a: int, b: int) -> int:
    return a * b

Type hints become JSON Schema automatically.

Async Tools

import httpx

@tool(description="Fetch user data")
async def get_user(user_id: str) -> dict:
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"https://api.example.com/users/{user_id}")
        return resp.json()

Context Access

Use get_context() for logging, progress, and cancellation:

import anyio
from dedalus_mcp import tool, get_context

@tool(description="Process files with progress")
async def process_files(paths: list[str]) -> dict:
    ctx = get_context()
    
    # Logging
    await ctx.info("Starting", data={"count": len(paths)})
    
    processed = 0
    try:
        # Progress tracking
        async with ctx.progress(total=len(paths)) as tracker:
            for path in paths:
                await anyio.sleep(0.01)  # Simulate work
                processed += 1
                await tracker.advance(1)
    except anyio.get_cancelled_exc_class():
        await ctx.warning("Cancelled", data={"processed": processed})
        raise
    
    return {"processed": processed}

Context Methods

Method Description
ctx.info(msg, data={}) Info-level log
ctx.debug(msg, data={}) Debug-level log
ctx.warning(msg, data={}) Warning-level log
ctx.progress(total=N) Progress tracker context manager
ctx.cancelled Check if request was cancelled

Resources

Resources provide read-only data for LLM context.

Basic Resource

from dedalus_mcp import resource

@resource("config://app", description="Application config")
def get_config() -> str:
    return json.dumps({"debug": True, "version": "1.0"})

Decorator Parameters

@resource(
    uri="file:///data/users.json",
    name="Users Database",
    description="All user records",
    mime_type="application/json"
)
def users_data() -> str:
    return json.dumps(load_users())

Binary Resources

@resource("image://logo", mime_type="image/png")
def logo() -> bytes:
    return Path("logo.png").read_bytes()

Resource Templates

For dynamic URIs:

from dedalus_mcp import resource_template

@resource_template(
    "user-profile",
    uri_template="resource://users/{user_id}",
    description="User profile by ID",
)
async def user_profile(user_id: str) -> str:
    user = await fetch_user(user_id)
    return json.dumps(user)

Prompts

Prompts are user-controlled message templates.

Basic Prompt

from dedalus_mcp import prompt

@prompt("summarize", description="Summarize content")
def summarize_prompt(arguments: dict[str, str] | None):
    text = (arguments or {}).get("text", "")
    return [("user", f"Please summarize:\n\n{text}")]

Multi-Message Prompt

@prompt("code-review", description="Review code for issues")
def code_review(arguments: dict[str, str] | None):
    code = (arguments or {}).get("code", "")
    language = (arguments or {}).get("language", "python")
    return [
        ("user", f"Review this {language} code for bugs and improvements:"),
        ("user", f"```{language}\n{code}\n```"),
    ]

Async Prompt with Context

@prompt("generate-report", description="Generate a report")
async def generate_report(arguments: dict[str, str] | None):
    ctx = get_context()
    await ctx.info("Rendering prompt", data={"args": arguments or {}})
    return [("user", "Generate a concise weekly report for this project.")]

Server Registration

Modular Pattern

Dedalus decouples decoration from registration:

from dedalus_mcp import MCPServer, tool

# Decorator just attaches metadata
@tool(description="Add numbers")
def add(a: int, b: int) -> int:
    return a + b

# collect() registers with server
server = MCPServer("calculator")
server.collect(add)

Same tool can be registered with multiple servers:

server_a.collect(add)
server_b.collect(add)

Collect from Modules

from tools import math_tools, text_tools

server = MCPServer("multi")
server.collect_from(math_tools, text_tools)

Binding Context Manager

server = MCPServer("my-server")

with server.binding():
    # All decorated functions here auto-register
    @tool(description="...")
    def my_tool(): ...

Running the Server

server = MCPServer("my-server")
server.collect(add, multiply, get_config)

if __name__ == "__main__":
    import asyncio
    asyncio.run(server.serve())

Default: http://127.0.0.1:8000/mcp

Custom Port

asyncio.run(server.serve(port=9000))

Testing

Unit Test Tools Directly

Tools are just functions:

def test_add():
    assert add(2, 3) == 5

async def test_async_tool():
    result = await fetch_user("123")
    assert result["id"] == "123"

Test Registration

def test_registration():
    server = MCPServer("test")
    server.collect(add, multiply)
    
    names = list(server.tool_names)
    assert "add" in names
    assert "multiply" in names

Isolation

Each test gets a clean state (no globals):

def test_a():
    server = MCPServer("test-a")
    server.collect(tool_a)
    # No teardown needed

def test_b():
    server = MCPServer("test-b")
    server.collect(tool_b)
    # Completely independent

Connecting with Dedalus SDK

from dedalus_labs import AsyncDedalus, DedalusRunner

client = AsyncDedalus()
runner = DedalusRunner(client)

# Hosted MCP server (marketplace slug)
response = await runner.run(
    input="Search for auth docs",
    model="anthropic/claude-sonnet-4-20250514",
    mcp_servers=["your-org/your-server"],
)

# Local MCP server
response = await runner.run(
    input="Add 5 and 3",
    model="openai/gpt-4o-mini",
    mcp_servers=["http://localhost:8000/mcp"],
)

References

Install via CLI
npx skills add https://github.com/adamsardo/dedalus-skills --skill use-dedalus-mcp
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator