Skip to main content

Framework for creating dynamic AI agents with access to broad range of capabilities.

Project description

AdeptAI

Create evolving AI agents that can choose what capabilities they need to complete a task, dynamically updating their instructions, context data and tools.

Use MCP servers as capabilities with flexible customization of which tools and resources to include. All the complexity of MCP session lifecycle management, communication, tool calling, resource retrieval and server notification handling is taken care of automatically.

Overview

The two basic concepts involved are:

  • Capability - A collection of associated tools and context data, along with instructions and examples for how to use them. Look here to learn about the different types.
  • AgentBuilder - Translates a role and set of capabilities into an aggregated dynamically evolving system prompt and set of tools that can be used to define an AI agent's behaviour.

An agent can be configured with many capabilities to handle a broad range of tasks, without getting overwhelmed by context and tool choice since it can enable only the capabilities it needs to get its job done.

Integrates with your favourite agent framework, or greatly simplifies the process of building powerful AI agents directly with a model provider's SDK or API.

diagram

Installation

For basic installation:

pip install adept-ai

Can also specify the following optional dependencies depending on which framework you are using: [langchain, openai, pydantic_ai, composio]

Example

Here's an example AgentBuilder configuration with a combination of inbuilt, MCP-based and Composio capabilities:

import os
from adept_ai import AgentBuilder
from adept_ai.capabilities FileSystemCapability, StdioMCPCapability, ComposioActionsCapability

ROLE = "You are a helpful assistant with access to a range of capabilities that you should use to complete the user's request."

agent_builder = AgentBuilder(
    role=ROLE,
    capabilities=[
        FileSystemCapability(),
        # Register here to get API key: https://developer.accuweather.com/
        StdioMCPCapability(
            name="AccuWeather",
            description="Get weather data from AccuWeather, a weather forecasting and weather information service.",
            command="npx",
            args=["-y", "@timlukahorstmann/mcp-weather"],
            env={"ACCUWEATHER_API_KEY": os.getenv("ACCUWEATHER_API_KEY", "")},
            instructions=["Use a sessionId of '123' when calling tools that require one."]
        ),
        # Check here for setup instructions: https://github.com/GongRzhe/Gmail-MCP-Server
        StdioMCPCapability(
            name="Gmail",
            description="Access my Gmail account to read and send emails.",
            command="npx",
            args=["@gongrzhe/server-gmail-autoauth-mcp"],
            tools=["search_emails", "read_email", "send_email", "delete_email", "batch_delete_emails"],
            instructions=[
                "Use `older_than:` or `newer_than:` instead of `after:` and `before:` for relative time queries"],
        ),
        ComposioActionsCapability(
            name="WebSearch",
            description="Search the web for information about a topic.",
            actions=["COMPOSIO_SEARCH_SEARCH"],
        ),
    ],
)
# Provides an `enable_capabilities()` tool along with tools belonging to all enabled capabilities
tools = await agent_builder.get_tools()
# Generates dynamic system prompt that includes instructions and usage examples of each enabled capability
system_prompt = await agent_builder.get_system_prompt()

This can be easily used to make a simple agent that when provided a prompt like:

Check my calendar for upcoming events for the next 2 days, check the weather forceast in London at the time of each event, and write a HTML file with a formatted table listing the events with weather information for each

Would produce the following output (with logging enabled):

> Running tool: 'enable_capabilities' with args: {'capabilities': ['GoogleCalendar', 'AccuWeather', 'Filesystem']}
> Starting MCP server: GoogleCalendar
> Starting MCP server: AccuWeather
> Running tool: 'GoogleCalendar-list_events' with args: {'timeMin': '2025-06-06T00:00:00Z', 'timeMax': '2025-06-08T00:00:00Z'}
> Running tool: 'AccuWeather-weather-get_hourly' with args: {'location': 'London, GB', 'units': 'metric'}
> Running tool: 'Filesystem-create_file' with args: {'path': 'calendar_weather_report.html', 'content': '<!DOCTYPE html>\n<html lang="en">...</html>'}
> Stopping MCP server: AccuWeather 
> Stopping MCP server: GoogleCalendar
I have created an HTML file named calendar_weather_report.html that contains a formatted table listing your upcoming events for the next two days, along with the weather forecast in London for each event. You can open this file to view the details.       

Features

  • Fully typed and async
  • Customizable system prompt template
  • Helpful utilities for compatibility with LangGraph & PydanticAI frameworks and OpenAI SDK
  • Auto-create tools from existing sync or async functions/methods
  • Built-in Filesystem

Capabilities

Capabilities are configurable and stateful objects which provide a set of tools along with associated context data, instructions and usage examples.

MCP Capabilities

MCP servers are a great way to add functionality and integrate with external services, but there is a lot of boilerplate and complexity involved with managing their lifecycle and interaction, especially if you want to use multiple at the same time. Additionally, most agent frameworks only offer very basic MCP integration without support for flexibility or customisation. This makes MCP servers significantly less useful if they don't work exactly the way you need them to, or if models find them difficult to use reliably due to unintuitive function specifications.

In fact, models failed to reliably use tools from 3 out of 4 of the randomly chosen sample MCP servers used in the usage example without adding usage instructions.

AdeptAI's MCP-based capabilities make it easy to reliably and flexibly integrate MCP servers with your agent.

  • Supports both STDIO (StdioMCPCapability) and HTTP (HTTPMCPCapability) MCP servers
  • Choose which tools and resources to provide to the agent
  • Add description, instructions and usage examples to help agent use MCP tools reliably
  • Supports multiple concurrent MCP servers with advanced MCP session lifecycle management including on-demand initialisation only when they are enabled.
  • Handle server sampling requests (so MCP server can make request to LLM/agent)
  • Automatic caching of tool and resource lists, with handling of server notifications to reset caches (not even officially supported by the MCP SDK yet)
  • Create custom subclasses to customize the behaviour of MCP tools / resources and how they are presented to the agent

MCP capabilities can also be used in isolation as powerful MCP clients:

async with StdioMCPCapability(...) as mcp_client:
    tools = mcp_client.get_tools()

    first_tool = tools[0]
    result = await first_tool.call(arg1="foo", arg2="bar")

    resources = await mcp_client.list_all_resources()
    for resource in resources:
        resource_content = await mcp_client.read_resource(resource.uri)
        print(resource_content)

Composio Actions Capability

The ComposioActionsCapability allows simple integration with a broad range of external services, by providing a set of Composio actions as tools. The capability description can be generated based on apps or actions if not specified. Example:

ComposioToolsCapability(
    name="WebSearch",
    description="Search the web for information about a topic.",
    actions=["COMPOSIO_SEARCH_SEARCH"],
)
ComposioToolsCapability(name="GoogleSheets", apps=["GOOGLESHEETS"])

Built-in Capabilities

Choose from a collection of powerful built-in capabilities:

  • FileSystemCapability - Allows reading & writing files, with dynamically updating directory tree context data
  • FunctionToolsCapability - Conveniently create a capability with tools automatically generated from a set of provided functions.
  • More TBA

Custom Capabilities

Subclass Capability to create your own infinitely customisable capabilities to suit your needs, with greater flexibility than FunctionToolsCapability such as being configurable and having state.

Usage Examples

There is an included example AgentBuilder configuration with a collection of sample capabilities, and examples of using it with various agent frameworks.

The agent can then be run via CLI using command:

python -m examples.cli --framework [langchain|openai|pydantic_ai] <request prompt>

Using with OpenAI SDK

Includes an OpenAITools helper class to translate tools to OpenAI SDK format and handle tool execution.

from openai import AsyncOpenAI
from openai.types.responses import EasyInputMessageParam

from adept_ai.compat.openai import OpenAITools
from examples.agent_builder import get_agent_builder


async def run_openai(prompt: str, model_name: str, api_key: str):
    client = AsyncOpenAI(api_key=api_key)

    async with get_agent_builder() as agent_builder:
        message_history = [EasyInputMessageParam(role="user", content=prompt)]
        # Agent tool calling loop
        while True:
            # Retrieve tools within loop so they can be dynamically updated
            tools = await agent_builder.get_tools()
            openai_tools = OpenAITools(tools)
            system_prompt = await agent_builder.get_system_prompt()

            response = await client.responses.create(
                model=model_name,
                input=[EasyInputMessageParam(role="system", content=system_prompt)] + message_history,
                tools=openai_tools.get_responses_tools(),
            )

            for output in response.output:
                if output.type == "function_call":
                    # Call the tool
                    result = await openai_tools.handle_function_call_output(output)
                    # Add the tool response to the message history
                    message_history.append(output)
                    message_history.append(result)
                else:
                    print(f"AI Agent: {response.output_text}")
                    break

See code here.

Using with LangChain/LangGraph

The prebuilt create_react_agent() does not support dynamic updating of tools within a run, so interrupt_after is used as a workaround to achieve this. It would be possible to create a custom graph-based agent that supports this more natively.

from langchain_core.messages import HumanMessage, ToolMessage
from langgraph.prebuilt import create_react_agent

from adept_ai.compat.langchain import tool_to_langchain_tool
from examples.agent_builder import get_agent_builder
from examples.langchain.models import get_model_from_name_and_api_key


async def run_langchain(prompt: str, model_name: str, api_key: str):
    model = get_model_from_name_and_api_key(model_name=model_name, api_key=api_key)

    async with get_agent_builder() as builder:
        messages = [HumanMessage(content=prompt)]

        while True:
            agent = create_react_agent(
                model,
                tools=[tool_to_langchain_tool(tool) for tool in await builder.get_tools()],
                prompt=await builder.get_system_prompt(),
                # Interrupt after tool calls to dynamically rebuild agent with new tools and system prompt
                interrupt_after=["tools"],
            )
            response = await agent.ainvoke({"messages": messages})

            if isinstance(response["messages"][-1], ToolMessage):
                messages = response["messages"]
                # Continue agent tool calling loop with refreshed system prompt and tools
                continue
            else:
                message = response["messages"][-1].content
                print(f"AI Agent: {message}")
                break

See code here.

Using with PydanticAI

The get_pydantic_ai_tools() helper function uses PydanticAI's dynamic function tools feature, however requires all tool definitions to be built up-front, meaning MCP servers & sessions for all MCP capabilities will need to be initialised even if they are not enabled.
A custom wrapper is also added to the tools which causes the system prompt to be dynamically updated within an agent run.

from pydantic_ai import Agent

from adept_ai.compat.pydantic_ai import get_pydantic_ai_tools
from examples.agent_builder import get_agent_builder

from examples.pydantic_ai.models import build_model_from_name_and_api_key


async def run_pydantic_ai(prompt: str, model_name: str | None, api_key: str | None = None):
    # Build the model from name and API key
    model = build_model_from_name_and_api_key(model_name, api_key)

    async with get_agent_builder() as builder:
        agent = Agent(model=model, tools=await get_pydantic_ai_tools(builder), instrument=True)

        # Configure the dynamic system prompt
        agent.instructions(builder.get_system_prompt)

        response = await agent.run(prompt)

        print(f"AI Agent: {response.output}")

See code here.

Advanced

Customise MCP Capabilities

You can create custom StdioMCPCapability or HttpMCPCapability subclasses for a specific MCP server to do things like:

  • Customize the behaviour of specific tools, by defining a tool that wraps one or more MCP server tools with arbitrary logic
  • Create a hybrid capability which has a mix of MCP and non-MCP tools (and resources)

Future Work

General

  • More logging / observability

MCP Capability

  • Subscribe to resources for automatic dynamic updates and smart caching
  • Dynamic / Templated resources
  • MCP prompts
  • Tool to allow agent to read resources

License

MIT License

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

adept_ai-0.2.3.tar.gz (28.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

adept_ai-0.2.3-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file adept_ai-0.2.3.tar.gz.

File metadata

  • Download URL: adept_ai-0.2.3.tar.gz
  • Upload date:
  • Size: 28.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.10

File hashes

Hashes for adept_ai-0.2.3.tar.gz
Algorithm Hash digest
SHA256 f6d0b5449db174af3e0c12406297ff98677bb2738751839513c750cda668bb23
MD5 e8363f8812d051a823af876674e17197
BLAKE2b-256 164b45f778270c41c32a4fff3e09015a07aece136e5f12f49e949b56e6449a9f

See more details on using hashes here.

File details

Details for the file adept_ai-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: adept_ai-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.10

File hashes

Hashes for adept_ai-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c9bd10d48a7330a397d0cd32baff8e3b20a5ad780150767d17a1a93eab6a3055
MD5 5a7527a09b6a79b0d3def0bad253b3e1
BLAKE2b-256 4aa6ed3600ad4cf888f61246f51f5b48f6920daff5547a88dc831762fec3501e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page