# How OpenWork MCP Exposes `search_capabilities` and `execute_capability` for Agent Integration

> Learn how OpenWork MCP exposes search_capabilities and execute_capability for seamless agent integration. Discover and invoke API capabilities type-safely without hardcoding.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: api-reference
- Published: 2026-08-15

---

**OpenWork MCP exposes exactly two tools—`search_capabilities` and `execute_capability`—through the `/mcp/agent` endpoint, giving AI agents a searchable, type-safe way to discover and invoke API capabilities without hardcoding endpoint details.**

OpenWork's Model Context Protocol (MCP) implementation provides a minimal, deterministic surface for agent integration. Rather than exposing raw API endpoints, the [[`ee/apps/den-api/src/mcp/agent.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/mcp/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) module advertises only two tools that agents use to find and execute capabilities. This article breaks down how these tools work, complete with source code paths and runnable examples.

---

## The Two-Tool Surface

OpenWork MCP deliberately limits agent exposure to two operations:

| Tool | Defined In | Purpose |
|------|-----------|---------|
| `search_capabilities` | [[`search.ts`](https://github.com/different-ai/openwork/blob/main/search.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/search.ts) | Ranked, semantic search across the capability catalog |
| `execute_capability` | [[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) | Validated execution of a discovered capability |

This design follows a **search-first workflow**: agents must discover capabilities before invoking them, making integrations resilient to API changes.

---

## How `search_capabilities` Discovers Capabilities

The `search_capabilities` tool implements semantic search over OpenWork's internal OpenAPI catalog. Located in [[`ee/apps/den-api/src/mcp/search.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/mcp/search.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/search.ts), the `searchCapabilities` function scores operations against free-text queries.

### The Search Algorithm

1. **Tokenization** — The query string is split into searchable tokens
2. **Scoring** — Each `McpToolOperation` receives a relevance score based on:
   - **Name matches** (highest weight)
   - **Summary/description tokens**
   - **Path segment matches**
3. **Ranking and trimming** — Results sort by score and return a configurable limit (default: 5)

### SearchResponse Shape

Each `CapabilityMatch` in the response includes everything an agent needs to construct a valid call:

```json
{
  "name": "listWorkers",
  "method": "GET",
  "path": "/api/v1/workers",
  "pathParams": [],
  "queryParams": ["limit", "offset"],
  "hasBody": false,
  "bodySchema": null,
  "score": 0.94
}

```

### Example: Calling search_capabilities

```typescript
// Agent discovers capabilities matching "list workers"
const searchResult = await callTool(
  "https://api.openworklabs.com/mcp/agent",
  mcpToken,
  "search_capabilities",
  { 
    query: "list workers", 
    limit: 5 
  }
);

// Response contains ranked matches with full invocation metadata
const bestMatch = searchResult[0];  // Highest scoring capability
const capabilityName = bestMatch.name;  // "listWorkers"

```

The agent uses `name` for the subsequent `execute_capability` call and `pathParams`/`queryParams`/`hasBody` to construct valid arguments.

---

## How `execute_capability` Executes Discovered Tools

The `execute_capability` tool in [[`ee/apps/den-api/src/mcp/agent.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/mcp/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) validates and forwards agent requests to the underlying OpenAPI implementation.

### Validation Steps

1. **Name verification** — Confirms the supplied `name` exists in the current catalog (prevents stale capability calls)
2. **Argument validation** — Checks that provided `pathParams`, `queryParams`, and `body` match the operation's schema
3. **Schema drift detection** — Returns `unknown_capability` or `invalid_capability_arguments` if the capability changed since discovery

### Execution Flow

After validation, [[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) forwards to the generic OpenAPI invoker ([[`invoke.ts`](https://github.com/different-ai/openwork/blob/main/invoke.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/invoke.ts)), which:

- Substitutes path parameters into the URL template
- Appends query parameters
- Serializes and sends the request body (if `hasBody`)
- Returns the raw HTTP response to the agent

### Example: Executing a Discovered Capability

```typescript
// Execute using the name and arguments from search_capabilities
const result = await callTool(
  "https://api.openworklabs.com/mcp/agent",
  mcpToken,
  "execute_capability",
  {
    name: "listWorkers",           // Must match search result exactly
    queryParams: { limit: 20 },    // Matches bestMatch.queryParams
    pathParams: {},                // Empty for this capability
    // body omitted: hasBody === false
  }
);

console.log(result);  // Raw API response: { workers: [...], total: 47 }

```

---

## Building the Capability Catalog

Both tools rely on the in-process catalog built at server startup. The [[`ee/apps/den-api/src/mcp/catalog.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/mcp/catalog.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/catalog.ts) module:

- Scrapes the OpenAPI specification for all registered operations
- Wraps each as a `McpToolOperation` with invocation metadata
- Makes the catalog available to `searchCapabilities` and execution validation

This centralized catalog ensures that `search_capabilities` and `execute_capability` always reference the same, up-to-date API surface.

---

## Security and Policy Enforcement

All MCP calls pass through OpenWork's policy layer before reaching business logic:

- **Permission checks** verify the caller's access rights to each capability
- **Rate limiting** applies per-tool and per-tenant quotas
- **Credential isolation** ensures agents never receive raw API tokens—authentication happens server-side in [[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts)

The minimal two-tool surface reduces attack exposure by preventing agents from directly constructing arbitrary HTTP requests.

---

## Complete Agent Integration Flow

A production agent implementation combines both tools in a resilient discovery-execution loop:

```typescript
async function invokeOpenWorkCapability(
  query: string,
  args: Record<string, unknown>
) {
  // 1️⃣ DISCOVER: Find capabilities matching natural language query
  const matches = await callTool(
    "https://api.openworklabs.com/mcp/agent",
    mcpToken,
    "search_capabilities",
    { query, limit: 3 }
  );

  if (matches.length === 0) {
    throw new Error(`No capabilities found for: ${query}`);
  }

  const capability = matches[0];
  console.log(`Selected: ${capability.name} (score: ${capability.score})`);

  // 2️⃣ VALIDATE: Check argument compatibility
  const providedArgs = Object.keys(args);
  const requiredArgs = [
    ...capability.pathParams,
    ...(capability.queryParams || [])
  ];
  
  const missing = requiredArgs.filter(p => !(p in providedArgs));
  if (missing.length > 0) {
    throw new Error(`Missing required arguments: ${missing.join(', ')}`);
  }

  // 3️⃣ EXECUTE: Call the validated capability
  const result = await callTool(
    "https://api.openworklabs.com/mcp/agent",
    mcpToken,
    "execute_capability",
    {
      name: capability.name,
      pathParams: args.pathParams || {},
      queryParams: args.queryParams || {},
      body: capability.hasBody ? args.body : undefined
    }
  );

  return result;
}

// Usage
const workers = await invokeOpenWorkCapability(
  "list workers", 
  { queryParams: { limit: 50 } }
);

```

---

## MCP Endpoint Discovery

Agents register OpenWork MCP at the standardized endpoint:

```

GET https://api.openworklabs.com/mcp/agent

```

The tool list response confirms both capabilities are available:

```json
{
  "tools": [
    { "name": "search_capabilities" },
    { "name": "execute_capability" }
  ]
}

```

This format is validated by the test suite in [[`connect-debug-proxy.test.ts`](https://github.com/different-ai/openwork/blob/main/connect-debug-proxy.test.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/connect-debug-proxy.test.ts), ensuring consistent agent onboarding.

---

## Summary

- **Two-tool design**: `search_capabilities` and `execute_capability` form the complete OpenWork MCP surface, defined in [[`search.ts`](https://github.com/different-ai/openwork/blob/main/search.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/search.ts) and [[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts)
- **Search-first workflow**: Agents discover capabilities by natural language query before execution, preventing hardcoded endpoint dependencies
- **Rich metadata**: Search results include `name`, `method`, `path`, parameter requirements, and `bodySchema` for self-contained invocation
- **Server-side validation**: [[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) validates capability names and arguments against the live catalog, detecting schema drift
- **Secure by default**: Policy enforcement, rate limiting, and credential isolation happen transparently in the MCP layer

---

## Frequently Asked Questions

### How does an agent know which arguments to pass to `execute_capability`?

The `search_capabilities` response includes `pathParams`, `queryParams`, and `hasBody` for every match. Agents inspect these fields from the selected `CapabilityMatch` to construct valid arguments. If the capability requires a JSON body, `bodySchema` contains the validation schema.

### What happens if a capability changes after an agent discovers it?

[[`agent.ts`](https://github.com/different-ai/openwork/blob/main/agent.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/agent.ts) validates the capability name against the current catalog before execution. If the name no longer exists or the arguments don't match the updated schema, the server returns `unknown_capability` or `invalid_capability_arguments`. Agents should trap these errors and call `search_capabilities` again to refresh their capability cache.

### Can agents call `execute_capability` without calling `search_capabilities` first?

Technically yes, but discouraged. The agent must know the exact capability `name` and argument structure. Since names and schemas evolve, direct execution risks `unknown_capability` errors. The [[`AGENTS.md`](https://github.com/different-ai/openwork/blob/main/AGENTS.md)](https://github.com/different-ai/openwork/blob/dev/AGENTS.md) documentation explicitly recommends the search-then-execute pattern for forward compatibility.

### Where is the capability catalog sourced from?

[[`catalog.ts`](https://github.com/different-ai/openwork/blob/main/catalog.ts)](https://github.com/different-ai/openwork/blob/dev/ee/apps/den-api/src/mcp/catalog.ts) builds the catalog from OpenWork's internal OpenAPI specification at server startup. The catalog includes all `McpToolOperation` instances with their HTTP method, path, parameters, and request body schemas, making it the single source of truth for both search and execution.