Skills Protocol — Full Specification (v0.1)

A minimal protocol for connecting LLM agents to Skills (instructions + code) and Blobs (large data) via a Skills Runtime with sandboxed code execution.

The protocol is JSON-RPC 2.0 over HTTP.

This specification includes:

  • Explicit LLM tool-call interface
  • Canonical Skills Protocol Guide skill
  • Bootstrap tool load_skills_protocol_guide
  • Appendix with recommended system prompt snippet

Core Concepts

Skill

A Skill is a versioned bundle containing:

  • skill.toml — machine-readable manifest (id, runtime, permissions, metadata)
  • SKILL.md — instructions & examples for the LLM
  • code/ — optional executable implementation
  • resources/ — optional supporting files (extra docs, schemas, etc.)

Skills come in two kinds:

  • Action skills — have a runtime and can be executed
  • Instruction skills — only provide context (no runtime)

Blob

A Blob is an opaque handle (blob:<id>) to large data (text, binary, JSON). Blobs live in the runtime's storage and can be mounted into sandboxes. Large documents, logs, CSVs, etc. should be represented as blobs rather than inline JSON. Skills and code operate on blobs without passing large data through the LLM.

Run

A Run is a single ephemeral sandbox execution:

  • Either via execute_skill — run a skill's entrypoint
  • Or via run_code — run LLM-written code that can import skills

Each run has:

  • run_id — stable identifier
  • status"completed" or "failed"
  • summary — short human-readable description
  • Optional output, output_blobs, logs_preview, and error

Skills Runtime

A Skills Runtime is a server implementing this spec. It:

  • Stores Skills and Blobs
  • Creates sandboxed execution environments
  • Exposes a JSON-RPC 2.0 API
  • Provides an LLM-visible tool surface

LLM Tool-Call Interface (Normative)

Runtimes MUST expose these tools to the LLM:

  1. list_skills — enumerate available skills
  2. describe_skill — retrieve skill manifest and documentation
  3. read_skill_file — read any file in a skill's directory
  4. execute_skill — run a skill's entrypoint
  5. run_code — execute LLM-written code with mounted skills
  6. create_blob — store large content
  7. read_blob — retrieve blob previews
  8. load_skills_protocol_guide — bootstrap instruction tool

These are the only LLM tools. Skills do not appear as native tools to the LLM.


Tool Schemas

The following schemas define the tool interface for LLMs. Runtimes SHOULD expose these in a format compatible with their LLM integration (e.g., OpenAI function calling, Anthropic tool use, etc.).

list_skills

{
  "name": "list_skills",
  "description": "Enumerate available skills, optionally filtering by namespace. Results are deterministically sorted.",
  "parameters": {
    "type": "object",
    "properties": {
      "namespace": {
        "type": "string",
        "description": "Optional namespace to filter skills (e.g., 'salesforce')"
      },
      "detail": {
        "type": "string",
        "enum": ["names", "summary"],
        "default": "names",
        "description": "Level of detail to return"
      },
      "limit": {
        "type": "integer",
        "default": 50,
        "description": "Maximum number of results"
      },
      "cursor": {
        "type": "string",
        "description": "Pagination cursor from previous response"
      }
    }
  }
}

describe_skill

{
  "name": "describe_skill",
  "description": "Retrieve a skill's manifest and documentation frontmatter.",
  "parameters": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Skill name (e.g., 'salesforce.leads.sync')"
      },
      "version": {
        "type": "string",
        "description": "Skill version (omit for latest)"
      },
      "detail": {
        "type": "string",
        "enum": ["manifest", "summary", "full"],
        "default": "summary",
        "description": "Level of detail to return"
      }
    },
    "required": ["name"]
  }
}

read_skill_file

{
  "name": "read_skill_file",
  "description": "Read any file in a skill's directory (e.g., SKILL.md, extra docs, schemas).",
  "parameters": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Skill name"
      },
      "version": {
        "type": "string",
        "description": "Skill version (omit for latest)"
      },
      "path": {
        "type": "string",
        "description": "Path to file within skill directory (e.g., 'SKILL.md')"
      }
    },
    "required": ["name", "path"]
  }
}

execute_skill

{
  "name": "execute_skill",
  "description": "Execute a skill's entrypoint in an ephemeral sandbox.",
  "parameters": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Skill name"
      },
      "version": {
        "type": "string",
        "description": "Skill version (omit for latest)"
      },
      "args": {
        "type": "object",
        "description": "JSON-serializable arguments to pass to the skill"
      },
      "input_blobs": {
        "type": "array",
        "items": {"type": "string"},
        "description": "Blob IDs to mount in the sandbox"
      },
      "timeout_ms": {
        "type": "integer",
        "description": "Execution timeout in milliseconds"
      }
    },
    "required": ["name"]
  }
}

run_code

{
  "name": "run_code",
  "description": "Execute LLM-written code in a sandbox with specified skills and blobs mounted.",
  "parameters": {
    "type": "object",
    "properties": {
      "language": {
        "type": "string",
        "enum": ["python"],
        "description": "Programming language (MVP: python only)"
      },
      "code": {
        "type": "string",
        "description": "Source code to execute"
      },
      "entrypoint": {
        "type": "string",
        "default": "main",
        "description": "Function name to call"
      },
      "args": {
        "type": "object",
        "description": "Arguments to pass to entrypoint function"
      },
      "mount_skills": {
        "type": "array",
        "items": {"type": "string"},
        "description": "Skill names to mount in sandbox"
      },
      "input_blobs": {
        "type": "array",
        "items": {"type": "string"},
        "description": "Blob IDs to mount in sandbox"
      },
      "limits": {
        "type": "object",
        "properties": {
          "timeout_ms": {"type": "integer"}
        },
        "description": "Execution limits"
      }
    },
    "required": ["language", "code"]
  }
}

create_blob

{
  "name": "create_blob",
  "description": "Store large content as a blob and return its ID.",
  "parameters": {
    "type": "object",
    "properties": {
      "content": {
        "type": "string",
        "description": "Content to store"
      },
      "kind": {
        "type": "string",
        "description": "MIME type (e.g., 'text/plain', 'application/json')"
      }
    },
    "required": ["content", "kind"]
  }
}

read_blob

{
  "name": "read_blob",
  "description": "Retrieve a preview of blob content. Full reads discouraged for large blobs.",
  "parameters": {
    "type": "object",
    "properties": {
      "blob_id": {
        "type": "string",
        "description": "Blob identifier"
      },
      "mode": {
        "type": "string",
        "enum": ["sample_head", "sample_tail", "full"],
        "default": "sample_head",
        "description": "How to sample the blob"
      },
      "max_bytes": {
        "type": "integer",
        "default": 2000,
        "description": "Maximum bytes to return"
      }
    },
    "required": ["blob_id"]
  }
}

load_skills_protocol_guide

{
  "name": "load_skills_protocol_guide",
  "description": "Load the Skills Protocol Guide to learn how to use these tools. Call this first if you haven't read the guide yet.",
  "parameters": {
    "type": "object",
    "properties": {}
  }
}

Python Client Example

Here's a minimal example of how an LLM agent would interact with a Skills Runtime using these tools:

import requests

class SkillsRuntimeClient:
    def __init__(self, endpoint="http://localhost:8080/rpc"):
        self.endpoint = endpoint
        self.request_id = 0

    def _call(self, method, params=None):
        self.request_id += 1
        payload = {
            "jsonrpc": "2.0",
            "id": str(self.request_id),
            "method": method,
            "params": params or {}
        }
        response = requests.post(self.endpoint, json=payload)
        result = response.json()

        if "error" in result:
            raise Exception(f"RPC Error: {result['error']}")
        return result["result"]

    def list_skills(self, namespace=None, detail="names", limit=50):
        return self._call("list_skills", {
            "namespace": namespace,
            "detail": detail,
            "limit": limit
        })

    def describe_skill(self, name, version=None, detail="summary"):
        params = {"name": name, "detail": detail}
        if version:
            params["version"] = version
        return self._call("describe_skill", params)

    def read_skill_file(self, name, path, version=None):
        params = {"name": name, "path": path}
        if version:
            params["version"] = version
        return self._call("read_skill_file", params)

    def execute_skill(self, name, args=None, version=None,
                     input_blobs=None, timeout_ms=None):
        params = {"name": name}
        if version:
            params["version"] = version
        if args:
            params["args"] = args
        if input_blobs:
            params["input_blobs"] = input_blobs
        if timeout_ms:
            params["timeout_ms"] = timeout_ms
        return self._call("execute_skill", params)

    def run_code(self, code, language="python", entrypoint="main",
                args=None, mount_skills=None, input_blobs=None,
                timeout_ms=None):
        params = {
            "language": language,
            "code": code,
            "entrypoint": entrypoint
        }
        if args:
            params["args"] = args
        if mount_skills:
            params["mount_skills"] = mount_skills
        if input_blobs:
            params["input_blobs"] = input_blobs
        if timeout_ms:
            params["limits"] = {"timeout_ms": timeout_ms}
        return self._call("run_code", params)

    def create_blob(self, content, kind="text/plain"):
        return self._call("create_blob", {
            "content": content,
            "kind": kind
        })

    def read_blob(self, blob_id, mode="sample_head", max_bytes=2000):
        return self._call("read_blob", {
            "blob_id": blob_id,
            "mode": mode,
            "max_bytes": max_bytes
        })

    def load_skills_protocol_guide(self):
        return self._call("load_skills_protocol_guide", {})


# Example usage
client = SkillsRuntimeClient()

# 1. Load the protocol guide (first-time usage)
guide = client.load_skills_protocol_guide()
print(guide["content"])

# 2. Discover available skills
skills = client.list_skills(namespace="salesforce")
print(f"Found {len(skills['skills'])} Salesforce skills")

# 3. Inspect a specific skill
skill_info = client.describe_skill("salesforce.leads.sync")
print(f"Skill: {skill_info['skill']['manifest']['description']}")

# 4. Read full documentation
docs = client.read_skill_file("salesforce.leads.sync", "SKILL.md")
print(docs["content"])

# 5. Create a blob for large data
csv_data = "name,email,company\nJohn Doe,john@example.com,Acme Inc\n..."
blob = client.create_blob(csv_data, kind="text/csv")
print(f"Created blob: {blob['blob_id']}")

# 6. Execute the skill
result = client.execute_skill(
    name="salesforce.leads.sync",
    args={
        "sheet_blob": blob["blob_id"],
        "env": "prod"
    },
    input_blobs=[blob["blob_id"]]
)

if result["status"] == "completed":
    print(f"Success: {result['summary']}")
    print(f"Output: {result['output']}")
else:
    print(f"Failed: {result['error']['message']}")

# 7. Run custom code that uses multiple skills
code = """
from skills.salesforce.leads.sync import sync_leads
from skills.gdrive.sheets.read import read_sheet
from runtime import blobs

def main(args):
    # Read data from Google Sheets
    sheet_data = read_sheet(args['sheet_url'])

    # Convert to blob
    blob_id = blobs.write_text(sheet_data)

    # Sync to Salesforce
    result = sync_leads(blob_id, env='prod')

    return {
        'synced': result['count'],
        'errors': result['errors']
    }
"""

result = client.run_code(
    code=code,
    mount_skills=["salesforce.leads.sync", "gdrive.sheets.read"],
    args={"sheet_url": "https://docs.google.com/spreadsheets/d/..."}
)

print(f"Custom workflow result: {result['output']}")

This example demonstrates:

  • Tool invocation via JSON-RPC
  • Typical LLM agent workflow: discover → inspect → execute
  • Both single-skill execution and multi-skill composition via run_code
  • Blob handling for large data

Skill Artifacts

Skill Layout

On disk or in a registry, each skill has:

<skill_root>/
  skill.toml
  SKILL.md
  code/        # optional
  resources/   # optional

skill.toml (MVP Schema)

Minimal schema for v0.1:

# Required identifiers
name        = "salesforce.leads.sync"       # globally unique skill id
version     = "0.1.0"
description = "Sync leads from a sheet blob into Salesforce."
kind        = "action"                      # "action" | "instruction"

# Optional metadata
namespace   = "salesforce"                  # for grouping / filtering
tags        = ["crm", "leads", "sync"]

# Optional runtime (required for action skills)
[runtime]
language   = "python"                       # MVP: "python" only
entrypoint = "code/main.py"                 # module path
export     = "main"                         # function name

# Optional: informal input schema (for LLM & humans)
[inputs]
sheet_blob = { type = "blob", description = "Blob id of the source sheet" }
env        = { type = "string", description = "Which Salesforce env" }

# Optional: permissions
[permissions]
network = ["https://*.salesforce.com"]      # network allowlist
secrets = ["SALESFORCE_API_KEY"]            # secret names the runtime may inject

Notes:

  • Instruction skills can omit [runtime] or set kind = "instruction"
  • For v0.1, runtimes MAY ignore inputs and permissions but SHOULD parse them

SKILL.md (MVP)

SKILL.md is markdown with YAML frontmatter at the top:

---
name: Salesforce Lead Sync
short_description: Sync leads from a sheet blob into Salesforce.
tags: [crm, leads, sync]
---

The body should briefly describe:

  • What the skill does
  • How to call it (expected arguments)
  • One or two example tasks

JSON-RPC Conventions

The Skills Protocol uses JSON-RPC 2.0 over HTTP.

Success and Failure

There are two levels of error:

  1. Protocol-level errors (JSON-RPC error):

    • Invalid method name
    • Invalid parameters
    • Authorization failures
    • Internal server errors unrelated to sandbox execution
  2. Run-level failures (in-band inside result):

    • Exceptions thrown by skill code
    • Validation errors in skill code
    • Timeouts or resource limits in the sandbox

Patterns:

// Successful RPC, run succeeded
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "status": "completed",
    "...": "..."
  }
}

// Successful RPC, run failed inside sandbox
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "status": "failed",
    "error": {
      "message": "Traceback...",
      "type": "RuntimeError"
    }
  }
}

// Protocol-level error
{
  "jsonrpc": "2.0",
  "id": "1",
  "error": {
    "code": -32602,
    "message": "Invalid params"
  }
}

Runtimes MUST:

  • Use top-level error only for protocol/transport issues
  • Use result.status = "failed" for sandbox / skill execution errors

Methods (MVP Surface)

All methods are invoked via JSON-RPC 2.0 at a single HTTP endpoint (e.g. /rpc).

Skill Discovery & Introspection

list_skills

Purpose: Enumerate available skills, optionally filtering by namespace.

Request:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "list_skills",
  "params": {
    "namespace": "salesforce",    // optional, string
    "detail": "names",            // "names" | "summary"
    "limit": 50,                  // optional, default 50
    "cursor": null                // optional, opaque pagination token
  }
}

Response (detail = "names"):

{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "skills": [
      {
        "name": "salesforce.leads.sync",
        "version": "0.1.0",
        "description": "Sync leads from a sheet blob into Salesforce.",
        "namespace": "salesforce",
        "kind": "action"
      }
    ],
    "next_cursor": null
  }
}

Requirements:

  • Results MUST be sorted deterministically (e.g. (namespace, name, version DESC))
  • Implementations MUST NOT perform semantic search here; this is a pure listing call

describe_skill

Purpose: Retrieve a skill's manifest and optional SKILL.md frontmatter/body.

Request:

{
  "jsonrpc": "2.0",
  "id": "2",
  "method": "describe_skill",
  "params": {
    "name": "salesforce.leads.sync",
    "version": "0.1.0",       // optional, latest if omitted
    "detail": "summary"       // "manifest" | "summary" | "full"
  }
}

Response (detail = "summary"):

{
  "jsonrpc": "2.0",
  "id": "2",
  "result": {
    "skill": {
      "manifest": { /* parsed skill.toml */ },
      "skill_md_frontmatter": { /* parsed YAML frontmatter */ }
    }
  }
}

If detail = "full", runtimes MAY include the full SKILL.md content inline, but are encouraged to keep responses small and rely on read_skill_file for large docs.

read_skill_file

Purpose: Read any file in a skill's directory (e.g. full SKILL.md, extra markdown, schemas).

Request:

{
  "jsonrpc": "2.0",
  "id": "3",
  "method": "read_skill_file",
  "params": {
    "name": "salesforce.leads.sync",
    "version": "0.1.0",
    "path": "SKILL.md"
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": "3",
  "result": {
    "content": "# Salesforce Lead Sync\n\n..."
  }
}

Runtimes MUST restrict path to the skill's directory and reject paths that attempt to escape (e.g. ../).

Skill Execution

execute_skill

Purpose: Run a skill's entrypoint in an ephemeral sandbox.

Request:

{
  "jsonrpc": "2.0",
  "id": "4",
  "method": "execute_skill",
  "params": {
    "name": "salesforce.leads.sync",
    "version": "0.1.0",             // optional, latest if omitted
    "args": {                       // JSON-serializable args
      "sheet_blob": "blob:sheet-123",
      "env": "prod"
    },
    "input_blobs": [                // optional blob ids to mount
      "blob:sheet-123"
    ],
    "timeout_ms": 300000            // optional, implementation default if omitted
  }
}

Response (success):

{
  "jsonrpc": "2.0",
  "id": "4",
  "result": {
    "status": "completed",
    "run_id": "run_9f2a",
    "summary": "Synced 742 leads (5 failed).",
    "output": {
      "updated_count": 742,
      "failed_count": 5
    },
    "output_blobs": [
      "blob:sync-log-42",
      "blob:failed-rows-1"
    ],
    "logs_preview": "Connected to Salesforce...\nSynced 742 leads...\n"
  }
}

Response (run failed):

{
  "jsonrpc": "2.0",
  "id": "4",
  "result": {
    "status": "failed",
    "run_id": "run_9f2a",
    "summary": "Exception while syncing leads.",
    "error": {
      "type": "RuntimeError",
      "message": "Traceback (most recent call last):\n..."
    },
    "logs_preview": "Starting sync...\nTraceback...\n"
  }
}

Requirements:

  • output MUST be kept small (e.g. < 4KB serialized). Larger data MUST be written to blobs
  • logs_preview SHOULD be truncated (e.g. < 2KB)
  • Runtimes MUST terminate sandbox execution if timeout_ms or internal resource limits are exceeded

Agent-Written Code

run_code

Purpose: Run LLM-written code in a sandbox, with specified skills and blobs mounted.

Request:

{
  "jsonrpc": "2.0",
  "id": "5",
  "method": "run_code",
  "params": {
    "language": "python",
    "code": "from skills.salesforce.leads.sync import sync_leads\nfrom runtime import blobs\n\ndef main(args):\n    ...",
    "entrypoint": "main",                // optional, default "main"
    "args": {
      "sheet_blob": "blob:sheet-123"
    },
    "mount_skills": [                    // skill ids to mount
      "salesforce.leads.sync",
      "gdrive.sheets.read"
    ],
    "input_blobs": [
      "blob:sheet-123"
    ],
    "limits": {
      "timeout_ms": 300000               // optional
    }
  }
}

Response: Same structure as execute_skill:

{
  "jsonrpc": "2.0",
  "id": "5",
  "result": {
    "status": "completed",
    "run_id": "run_abcd",
    "summary": "Updated 142 pending orders, 3 failed.",
    "output": {
      "updated_count": 142,
      "failed_count": 3
    },
    "output_blobs": [
      "blob:sync-log-42"
    ],
    "logs_preview": "Starting...\nUpdated 10...\n"
  }
}

Runtimes MUST:

  • Create a fresh sandbox for each run_code call
  • Mount requested skills and blobs (see Sandbox model below)
  • Expose a small runtime helper library inside the sandbox

Blobs

create_blob

Purpose: Store arbitrary content as a blob and return its ID.

Request:

{
  "jsonrpc": "2.0",
  "id": "6",
  "method": "create_blob",
  "params": {
    "content": "Discussed Q4 goals...\n[full transcript text]",
    "kind": "text/plain"           // or "application/json", etc.
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": "6",
  "result": {
    "blob_id": "blob:transcript-abc123",
    "size_bytes": 200000
  }
}

read_blob

Purpose: Retrieve a small view of a blob. Full reads are discouraged for large blobs.

Request:

{
  "jsonrpc": "2.0",
  "id": "7",
  "method": "read_blob",
  "params": {
    "blob_id": "blob:transcript-abc123",
    "mode": "sample_head",         // "sample_head" | "sample_tail" | "full"
    "max_bytes": 2000
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": "7",
  "result": {
    "content": "Discussed Q4 goals...\n[truncated]",
    "truncated": true,
    "kind": "text/plain"
  }
}

Runtimes SHOULD:

  • Support sample_head and sample_tail
  • Treat "full" as best-effort and MAY reject it for very large blobs

Bootstrap Instruction Tool

load_skills_protocol_guide

Purpose: Return the SKILL.md content of the canonical Skills Protocol Guide skill. This is a bootstrap instruction tool that teaches the LLM how to use the Skills Protocol.

Request:

{
  "jsonrpc": "2.0",
  "id": "8",
  "method": "load_skills_protocol_guide",
  "params": {}
}

Response:

{
  "jsonrpc": "2.0",
  "id": "8",
  "result": {
    "content": "# Skills Protocol Overview\n\nAvailable tools:\n\n1. `list_skills`\n..."
  }
}

This method MUST return the content of the canonical skills.protocol.guide skill's SKILL.md file.

Creating Skills (Optional)

create_skill

Purpose: Register a new skill programmatically (e.g. written by an agent).

Request:

{
  "jsonrpc": "2.0",
  "id": "9",
  "method": "create_skill",
  "params": {
    "manifest": { /* skill.toml parsed as JSON */ },
    "skill_md": "# My New Skill\n\n...",
    "files": [
      { "path": "code/main.py", "content": "def main(args):\n    ..." },
      { "path": "resources/examples.md", "content": "Example usage..." }
    ]
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": "9",
  "result": {
    "name": "user.julaiti.save_sheet_as_csv",
    "version": "0.1.0"
  }
}

Runtimes MUST:

  • Validate that path values stay within the skill root
  • Either persist the skill immediately or return a protocol-level error

Canonical Instruction Skill: skills.protocol.guide

Every Skills Runtime MUST include this canonical instruction skill to bootstrap LLM understanding of the protocol.

skill.toml

name        = "skills.protocol.guide"
version     = "0.1.0"
description = "Intro to the Skills Protocol for LLMs."
kind        = "instruction"
namespace   = "skills.protocol"
tags        = ["guide", "bootstrap"]

SKILL.md

---
name: Skills Protocol Guide
short_description: How to use the Skills Protocol tools.
---

# Skills Protocol Overview

Available tools:

1. `list_skills` — enumerate available skills
2. `describe_skill` — retrieve skill manifest and documentation
3. `read_skill_file` — read any file in a skill's directory
4. `execute_skill` — run a skill's entrypoint
5. `run_code` — execute LLM-written code with mounted skills
6. `create_blob` — store large content
7. `read_blob` — retrieve blob previews
8. `load_skills_protocol_guide` — bootstrap instruction tool (this guide)

## Recommended Workflow

1. **Discover skills**: Use `list_skills` to see what's available, optionally filtering by namespace
2. **Inspect documentation**: Use `describe_skill` and `read_skill_file` to understand how to use skills
3. **Run simple actions**: Use `execute_skill` for single-skill operations
4. **Write multi-step workflows**: Use `run_code` to compose multiple skills with custom logic
5. **Use blobs for large data**: Store and retrieve large content via `create_blob` and `read_blob`

## Best Practices

- Always use blobs for data larger than a few KB
- Keep `output` fields small; write large results to blobs
- Read skill documentation before executing
- Use deterministic skill discovery (no semantic search in `list_skills`)

Sandbox Execution Model (MVP)

For each execute_skill and run_code call, the runtime MUST:

Sandbox Creation

  1. Create a sandbox

    • Separate process or container
    • Non-root user
    • Enforce CPU, memory, and wall-clock time limits
  2. Mount resources

    • For each skill in mount_skills (and the target skill for execute_skill):
      • Mount the skill directory as /skills/<name>/ (read-only)
    • Create /workspace/ as a writable directory
    • Make blobs available:
      • Either mount as /blobs/<blob_id> files, and/or
      • Expose via helper functions in runtime.blobs
  3. Set environment

    • Inject only secrets listed in permissions.secrets for the executing skill
    • Optionally restrict outbound network to permissions.network
  4. Execute

    • For execute_skill:
      • Import the skill's runtime.entrypoint module and call export(args)
    • For run_code:
      • Save code as a temporary module, import it, and call entrypoint(args)
  5. Collect results

    • Capture return value as output (small JSON)
    • Capture stdout/stderr to build logs_preview (truncated)
    • Any blobs created via runtime helper functions become output_blobs
  6. Tear down

    • Destroy sandbox and all temporary files in /workspace/

In-Sandbox Helper Library (Python MVP)

Runtimes MUST provide a runtime package with at least:

# runtime/blobs.py
def read_text(blob_id: str) -> str: ...
def write_text(content: str) -> str: ...
def write_json(obj) -> str: ...

# runtime/log.py
def info(msg: str): ...
def error(msg: str): ...

These helpers:

  • Call back to create_blob under the hood (or equivalent internal API)
  • Append to logs that feed into logs_preview

Implementation Checklist (v0.1)

To implement a minimal but complete Skills Runtime:

Transport

  • JSON-RPC 2.0 over HTTP at /rpc

Storage

  • Skills stored as directories with skill.toml + SKILL.md
  • Blobs stored as files in a blob directory, named by random ids

Supported Methods

  • MUST: list_skills, describe_skill, read_skill_file
  • MUST: execute_skill, run_code
  • MUST: create_blob, read_blob
  • MUST: load_skills_protocol_guide
  • SHOULD: create_skill

Runtime

  • MVP: Python-only skills (runtime.language = "python")
  • Sandbox as:
    • New process with restricted environment, or
    • Container with resource limits
  • Use the filesystem layout described in section 7

Safety

  • Non-root execution
  • No network by default; allowlist from permissions.network if implemented
  • No secrets by default; inject only those named in permissions.secrets

Canonical Skill

  • Include skills.protocol.guide as described in section 6

Appendix A: Recommended System Prompt

When connecting an LLM to a Skills Runtime, include this snippet in the system prompt:

You are connected to a Skills Runtime using the Skills Protocol.

If you have not yet read the Skills Protocol Guide,
you MUST call the `load_skills_protocol_guide` tool before using
any other Skills Protocol tools.

After reading the guide, follow its workflow:
discover → inspect → run or write code → use blobs for large data.

Appendix B: Minimal Example

  1. User: "Sync these leads."
  2. LLM: upload CSV → create_blob
  3. LLM: list_skills(namespace="salesforce")
  4. LLM: describe_skill → inspect input format
  5. LLM: execute_skill
  6. LLM: return summary + preview of failed rows

Was this page helpful?