Skip to main content
CodeCall exposes four meta-tools that replace direct tool access. This page documents the complete API for each tool with request/response schemas and examples.

codecall:search

Search for tools by natural language query using semantic embeddings.

Request Schema

{
  "tool": "codecall:search",
  "input": {
    "query": "string (required) - Natural language search query",
    "topK": "number (optional, default: 5) - Maximum results to return",
    "filter": {
      "appIds": ["string"] // (optional) - Limit to specific apps
    }
  }
}

Response Schema

{
  "tools": [
    {
      "name": "string - Tool name (e.g., 'users:list')",
      "appId": "string - Owning app ID",
      "description": "string - Tool description",
      "score": "number - Relevance score (0-1)"
    }
  ],
  "totalIndexed": "number - Total tools in index"
}

Examples

{
  "tool": "codecall:search",
  "input": {
    "query": "get user profile information",
    "topK": 5
  }
}

Response Example

{
  "tools": [
    {
      "name": "users:getProfile",
      "appId": "user-service",
      "description": "Get user profile by ID with full details",
      "score": 0.94
    },
    {
      "name": "users:list",
      "appId": "user-service",
      "description": "List all users with pagination and filtering",
      "score": 0.78
    },
    {
      "name": "auth:whoami",
      "appId": "auth-service",
      "description": "Get current authenticated user info",
      "score": 0.72
    }
  ],
  "totalIndexed": 156
}

Search Strategy

CodeCall supports two embedding strategies:
StrategyDescriptionBest For
tfidfTF-IDF text matching (default)Fast, no model loading, works offline
embeddingSemantic embeddings via VectoriaDBBetter relevance, requires model
CodeCallPlugin.init({
  embedding: {
    strategy: 'tfidf',      // or 'embedding'
    similarityThreshold: 0.3,
  },
});

codecall:describe

Get detailed schemas and documentation for specific tools.

Request Schema

{
  "tool": "codecall:describe",
  "input": {
    "toolNames": ["string (required) - Array of tool names to describe"]
  }
}

Response Schema

{
  "tools": [
    {
      "name": "string - Tool name",
      "appId": "string - Owning app ID",
      "description": "string - Full description",
      "inputSchema": { /* JSON Schema */ },
      "outputSchema": { /* JSON Schema or null */ },
      "annotations": {
        "destructiveHint": "boolean (optional)",
        "idempotentHint": "boolean (optional)",
        "openWorldHint": "boolean (optional)"
      },
      "usageExamples": [
        {
          "description": "string - Example description",
          "code": "string - Example AgentScript code"
        }
      ]
    }
  ],
  "notFound": ["string (optional) - Tool names that weren't found"]
}
usageExamples returns up to 5 examples per tool. User-provided examples from the @Tool decorator take priority, with smart-generated examples filling remaining slots based on tool intent (create, list, get, update, delete, search).

Example

{
  "tool": "codecall:describe",
  "input": {
    "toolNames": ["users:list", "users:getById"]
  }
}

Security Note

codecall:describe will not return information about CodeCall meta-tools themselves. Attempting to describe codecall:execute returns the tool in notFound.

codecall:execute

Execute a JavaScript (AgentScript) plan that orchestrates multiple tools.

Request Schema

{
  "tool": "codecall:execute",
  "input": {
    "script": "string (required) - JavaScript code to execute",
    "context": { /* (optional) - Read-only context available as codecallContext */ },
    "allowedTools": ["string (optional) - Whitelist of allowed tool names"]
  }
}

Response Schema

Success:
{
  "status": "ok",
  "result": "any - Return value from script",
  "logs": ["string (optional) - Console output if enabled"]
}
Error Responses:
{
  "status": "syntax_error",
  "error": {
    "message": "Unexpected token at line 5",
    "location": {"line": 5, "column": 12}
  }
}
{
  "status": "illegal_access",
  "error": {
    "kind": "IllegalBuiltinAccess",
    "message": "Identifier 'eval' is not allowed in AgentScript"
  }
}
{
  "status": "runtime_error",
  "error": {
    "source": "script",
    "message": "Cannot read property 'name' of undefined",
    "name": "TypeError"
  }
}
{
  "status": "tool_error",
  "error": {
    "source": "tool",
    "toolName": "users:list",
    "message": "Rate limit exceeded",
    "code": "RATE_LIMIT"
  }
}
{
  "status": "timeout",
  "error": {
    "message": "Script execution timed out after 3500ms"
  }
}

Example

{
  "tool": "codecall:execute",
  "input": {
    "script": "const users = await callTool('users:list', { status: 'active' });\nconst admins = users.users.filter(u => u.role === 'admin');\nreturn { adminCount: admins.length, admins: admins.map(a => a.email) };"
  }
}

AgentScript API

Inside codecall:execute scripts, these functions are available:

callTool(name, input, options?)

Call a tool and get the result.
// Basic call
const users = await callTool('users:list', { limit: 100 });

// With error handling option
const result = await callTool('users:get', { id: 'invalid' }, {
  throwOnError: false  // Returns { success, data, error } instead of throwing
});

if (result.success) {
  return result.data;
} else {
  return { error: result.error.message };
}
Options:
OptionTypeDefaultDescription
throwOnErrorbooleantrueThrow on error or return result object

getTool(name)

Get metadata about a tool (name, description, schemas).
const meta = getTool('users:list');
console.log(meta.description);
console.log(meta.inputSchema);

codecallContext

Read-only context object passed in the request.
// Access context
const tenantId = codecallContext.tenantId;
const userId = codecallContext.userId;

// Cannot modify
codecallContext.tenantId = 'other'; // ❌ Throws error (frozen object)

console (if enabled)

Standard console methods, captured in response logs.
console.log('Processing users...');
console.warn('Large dataset detected');
console.error('Validation failed');
Console is only available if vm.allowConsole: true in plugin config. Logs are returned in the logs array of the response.

__safe_parallel(fns, options?)

Execute multiple async operations in parallel with controlled concurrency.
// Fetch multiple users in parallel
const userIds = ['user-1', 'user-2', 'user-3', 'user-4', 'user-5'];

const users = await __safe_parallel(
  userIds.map(id => () => callTool('users:get', { id })),
  { maxConcurrency: 3 }  // Max 3 concurrent requests
);

return users;  // Array of results in same order as input
Options:
OptionTypeDefaultMaxDescription
maxConcurrencynumber1020Max concurrent operations
Limits:
LimitValueError
Max array size100Cannot execute more than 100 operations in parallel
Max concurrency20Silently clamped
Error Handling:
// If any operation fails, the entire parallel call fails
try {
  const results = await __safe_parallel([
    () => callTool('users:get', { id: 'valid' }),
    () => callTool('users:get', { id: 'invalid' }),  // Throws
    () => callTool('users:get', { id: 'also-valid' }),
  ]);
} catch (error) {
  // Error message: "1 of 3 parallel operations failed:
  //   [1]: User not found"
}
Use Cases:
// Batch fetching
const ids = await callTool('users:listIds', { limit: 50 });
const users = await __safe_parallel(
  ids.map(id => () => callTool('users:get', { id }))
);

// Parallel aggregation
const [orders, users, metrics] = await __safe_parallel([
  () => callTool('orders:list', { status: 'pending' }),
  () => callTool('users:list', { role: 'admin' }),
  () => callTool('analytics:getMetrics', {}),
]);

codecall:invoke

Direct tool invocation without JavaScript execution. Useful for simple single-tool calls.

Request Schema

{
  "tool": "codecall:invoke",
  "input": {
    "tool": "string (required) - Tool name to invoke",
    "input": { /* (required) - Input to pass to the tool */ }
  }
}

Response Schema

Success:
{
  "status": "ok",
  "result": "any - Tool return value"
}
Error:
{
  "status": "error",
  "error": {
    "code": "string - Error code",
    "message": "string - Error message"
  }
}

Example

{
  "tool": "codecall:invoke",
  "input": {
  "tool": "users:getById",
  "input": {"id": "user-123"}
}
}

When to Use Invoke vs Execute

Use codecall:invokeUse codecall:execute
Single tool callMultiple tool calls
No data transformation neededFilter/join/transform results
Latency-sensitive (no VM overhead)Complex orchestration logic
Simple CRUD operationsConditional workflows

Error Codes Reference

Script Execution Errors

CodeStatusDescriptionRecovery
SYNTAX_ERRORsyntax_errorJavaScript syntax errorFix syntax at indicated location
VALIDATION_ERRORillegal_accessAST validation failedRemove blocked construct
SELF_REFERENCE_BLOCKEDillegal_accessTried to call codecall:* toolUse regular tools only
TOOL_NOT_FOUNDtool_errorTool doesn’t existCheck tool name spelling
ACCESS_DENIEDtool_errorTool not in allowedToolsAdd to allowlist
EXECUTION_ERRORruntime_errorRuntime error in scriptDebug script logic
TIMEOUTtimeoutExceeded time limitOptimize or increase timeout
TOOL_EXECUTION_ERRORtool_errorTool threw an errorCheck tool input

Worker Pool Errors (when adapter=‘worker_threads’)

Error ClassCodeDescriptionRecovery
WorkerTimeoutErrorWORKER_TIMEOUTExecution exceeded watchdog timeoutReduce script complexity
WorkerMemoryErrorWORKER_MEMORY_EXCEEDEDWorker exceeded memory limitReduce data size or increase limit
WorkerCrashedErrorWORKER_CRASHEDWorker process crashed unexpectedlyCheck for infinite recursion
WorkerPoolDisposedErrorPOOL_DISPOSEDPool was shut down during executionRestart the server
QueueFullErrorQUEUE_FULLExecution queue at capacityWait or scale up workers
QueueTimeoutErrorQUEUE_TIMEOUTWaited too long in queueScale up workers
ExecutionAbortedErrorEXECUTION_ABORTEDExecution was cancelledN/A (intentional)
MessageFloodErrorMESSAGE_FLOODRate limit exceededReduce callTool frequency
MessageValidationErrorMESSAGE_INVALIDMalformed message from workerReport as bug
MessageSizeErrorMESSAGE_TOO_LARGEMessage exceeded size limitReduce result size
WorkerStartupErrorWORKER_STARTUP_FAILEDWorker failed to initializeCheck system resources
TooManyPendingCallsErrorTOO_MANY_PENDING_CALLSToo many concurrent tool callsUse sequential calls

Error Handling Patterns

Basic Pattern: throwOnError

// Default: throws on error
const users = await callTool('users:list', { limit: 100 });

// Safe pattern: returns result object
const result = await callTool('users:get', { id: 'maybe-invalid' }, {
  throwOnError: false
});

if (result.success) {
  return { user: result.data };
} else {
  return { error: result.error.message, fallback: 'default-user' };
}

Retry Pattern

async function retryTool(toolName, args, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const result = await callTool(toolName, args, { throwOnError: false });

    if (result.success) {
      return result.data;
    }

    // Don't retry non-transient errors
    if (result.error.code === 'NOT_FOUND' || result.error.code === 'VALIDATION_ERROR') {
      throw new Error(result.error.message);
    }

    // Last attempt - throw
    if (attempt === maxRetries) {
      throw new Error(`Failed after ${maxRetries} attempts: ${result.error.message}`);
    }

    console.warn(`Attempt ${attempt} failed, retrying...`);
  }
}

// Usage
const data = await retryTool('api:fetch', { url: '/data' });

Fallback Pattern

// Try primary, fall back to secondary
const result = await callTool('cache:get', { key: 'users' }, { throwOnError: false });

const users = result.success
  ? result.data
  : await callTool('db:query', { table: 'users' });  // Fallback to DB

return users;

Partial Success Pattern

// Process items, collect both successes and failures
const ids = ['id-1', 'id-2', 'id-3', 'id-4'];
const results = { success: [], failed: [] };

for (const id of ids) {
  const result = await callTool('users:get', { id }, { throwOnError: false });

  if (result.success) {
    results.success.push(result.data);
  } else {
    results.failed.push({ id, error: result.error.message });
  }
}

return {
  users: results.success,
  errors: results.failed,
  successRate: results.success.length / ids.length,
};

Debugging Guide

Using console.log

// Enable in config
CodeCallPlugin.init({
  vm: { allowConsole: true }
});

// In script
const users = await callTool('users:list', { limit: 10 });
console.log('Fetched users:', users.length);

for (const user of users) {
  console.log('Processing:', user.id, user.email);
}

// Logs appear in response.logs array

Interpreting Error Messages

Cause: Loop ran more than maxIterations times Fix: Use pagination or filter data before looping
// Bad: looping over potentially large dataset
for (const item of items) { ... }

// Good: paginate or limit
const page = items.slice(0, 100);
for (const item of page) { ... }
Cause: Script called more than maxToolCalls tools Fix: Batch operations or use __safe_parallel
// Bad: one tool call per item
for (const id of ids) {
  await callTool('users:get', { id });
}

// Good: batch fetch
const users = await callTool('users:getBatch', { ids });
Cause: Script ran longer than timeoutMs Fix: Optimize, use less data, or increase timeout
// Check preset timeout
// locked_down: 2s, secure: 3.5s, balanced: 5s
CodeCallPlugin.init({
  vm: { preset: 'balanced', timeoutMs: 8000 }
});
Cause: Used a blocked identifier (eval, require, etc.) Fix: Use allowed alternatives
// Blocked
eval('code');
require('module');
setTimeout(fn, 100);

// Allowed
// Use callTool for external operations
await callTool('code:run', { script: 'code' });

Development Mode

For easier debugging during development:
CodeCallPlugin.init({
  vm: {
    preset: 'experimental',  // Longer timeouts, more iterations
    allowConsole: true,
  },

  // Don't sanitize errors in development
  sanitization: {
    removeStackTraces: process.env.NODE_ENV === 'production',
    removeFilePaths: process.env.NODE_ENV === 'production',
  },
});

Testing Strategy

Unit Testing AgentScript

import { Enclave } from 'enclave-vm';

describe('AgentScript', () => {
  let enclave: Enclave;

  beforeEach(() => {
    enclave = new Enclave({ securityLevel: 'STANDARD' });
  });

  afterEach(() => {
    enclave.dispose();
  });

  it('should execute basic script', async () => {
    const result = await enclave.execute(`
      return 1 + 1;
    `);

    expect(result.success).toBe(true);
    expect(result.value).toBe(2);
  });

  it('should call tools', async () => {
    const mockToolHandler = jest.fn().mockResolvedValue({ users: [] });

    const result = await enclave.execute(`
      const data = await callTool('users:list', {});
      return data.users.length;
    `, { toolHandler: mockToolHandler });

    expect(mockToolHandler).toHaveBeenCalledWith('users:list', {});
    expect(result.value).toBe(0);
  });
});

Integration Testing with Mock Tools

import { App, Tool, ToolContext } from '@frontmcp/sdk';
import { CodeCallPlugin } from '@frontmcp/plugins';

// Create test app with mock tools
@App({ id: 'test-app', plugins: [CodeCallPlugin.init()] })
class TestApp {
  @Tool({ name: 'users:list' })
  async listUsers(ctx: ToolContext) {
    return [
      { id: '1', name: 'Alice' },
      { id: '2', name: 'Bob' },
    ];
  }
}

describe('CodeCall Integration', () => {
  it('should execute scripts with real tools', async () => {
    const response = await callMcpTool('codecall:execute', {
      script: `
        const users = await callTool('users:list', {});
        return users.map(u => u.name);
      `,
    });

    expect(response.status).toBe('ok');
    expect(response.result).toEqual(['Alice', 'Bob']);
  });
});

Testing Security Rules

describe('Security', () => {
  it('should block eval', async () => {
    const result = await enclave.execute(`
      eval('1 + 1');
    `);

    expect(result.success).toBe(false);
    expect(result.error?.message).toContain('eval');
  });

  it('should block self-reference', async () => {
    const result = await callMcpTool('codecall:execute', {
      script: `
        await callTool('codecall:execute', { script: 'return 1' });
      `,
    });

    expect(result.status).toBe('illegal_access');
    expect(result.error.code).toBe('SELF_REFERENCE_BLOCKED');
  });

  it('should enforce iteration limit', async () => {
    const result = await enclave.execute(`
      let i = 0;
      for (const x of Array(100000)) { i++; }
      return i;
    `, { config: { maxIterations: 1000 } });

    expect(result.success).toBe(false);
    expect(result.error?.message).toContain('iteration limit');
  });
});

CI/CD Integration

# .github/workflows/test.yml
name: Test CodeCall Scripts

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci
      - run: npm test

      # Run CodeCall-specific security tests
      - run: npm run test:security
        env:
          CODECALL_VM_PRESET: secure

Rate Limiting

CodeCall meta-tools respect FrontMCP’s rate limiting:
import { RateLimitPlugin } from '@frontmcp/plugins';

@App({
  plugins: [
    CodeCallPlugin.init({ ... }),
    RateLimitPlugin.init({
      rules: [
        { tool: 'codecall:execute', limit: 10, window: '1m' },
        { tool: 'codecall:search', limit: 100, window: '1m' },
      ],
    }),
  ],
})