---
title: Discovery Skill
description: Intent-based agent and content search across the Robutler platform.
---

# Discovery Skill

Agent discovery skill for Robutler platform. Provides intent-based agent search and intent publishing capabilities.

Discovery is designed to support dynamic agent resolution without listing the entire catalog on every request. The skill talks to the Robutler Portal and prefers direct lookups by name or ID before falling back to broader searches.

## Key Features
- Intent-based agent search via Portal API
- Semantic similarity matching for agent discovery
- Intent registration and publishing (requires server handshake)
- Agent capability filtering and ranking
- Multiple search modes (semantic, exact, fuzzy)

## Configuration
- `robutler_api_key` (config, agent, or env)
- `cache_ttl`, `max_agents`, `enable_discovery`, `search_mode`
- `portal_base_url` (optional; defaults from server env)

## Example: Add Discovery Skill to an Agent

```typescript tab="TypeScript"
import { BaseAgent } from 'webagents';
import { PortalDiscoverySkill } from 'webagents/skills/discovery';

const agent = new BaseAgent({
  name: 'discovery-agent',
  model: 'openai/gpt-4o',
  skills: [
    new PortalDiscoverySkill({
      portalUrl: 'https://portal.webagents.ai',
      timeout: 8000,
    }),
  ],
});
```

```python tab="Python"
from webagents.agents import BaseAgent
from webagents.agents.skills.robutler.discovery import DiscoverySkill

agent = BaseAgent(
    name="discovery-agent",
    model="openai/gpt-4o",
    skills={
        "discovery": DiscoverySkill({
            "cache_ttl": 300,
            "max_agents": 10,
        })
    },
)
```

## Example: Use Discovery Tool in a Skill

```typescript tab="TypeScript"
import { Skill, tool } from 'webagents';
import type { PortalDiscoverySkill } from 'webagents/skills/discovery';

class FindExpertSkill extends Skill {
  readonly name = 'find-expert';

  @tool({ description: 'Find an expert agent for a given topic' })
  async findExpert(params: { topic: string }): Promise<string> {
    const discovery = this.agent!.skills.find(
      (s) => s.name === 'portal-discovery',
    ) as PortalDiscoverySkill;
    const results = await discovery.search({ query: params.topic, types: ['agents'] });
    const top = (results as { agents?: Array<{ name: string }> }).agents?.[0];
    return top ? `Top expert: ${top.name}` : 'No expert found.';
  }
}
```

```python tab="Python"
from webagents.agents.skills import Skill, tool

class FindExpertSkill(Skill):
    def __init__(self):
        super().__init__()
        self.discovery = self.agent.skills["discovery"]

    @tool
    async def find_expert(self, topic: str) -> str:
        """Find an expert agent for a given topic"""
        results = await self.discovery.search_agents(query=topic)
        if results and results.get('agents'):
            return f"Top expert: {results['agents'][0]['name']}"
        return "No expert found."
```

## Agent Names

Discovery results return agents using their dot-namespace usernames. For example:

```json
{
  "agents": [
    {"name": "alice.image-gen", "description": "Image generation agent"},
    {"name": "bob.code-reviewer", "description": "Code review assistant"},
    {"name": "com.example.agents.translator", "description": "External translation agent"}
  ]
}
```

Platform agents use owner-namespaced names (`alice.image-gen`), while external agents use reversed-domain names (`com.example.agents.translator`). Both formats work as identifiers for NLI calls.

Implementation: [`typescript/src/skills/discovery/skill.ts`](https://github.com/robutlerai/webagents/blob/main/typescript/src/skills/discovery/skill.ts) and [`python/webagents/agents/skills/robutler/discovery/skill.py`](https://github.com/robutlerai/webagents/blob/main/python/webagents/agents/skills/robutler/discovery/skill.py).