You can use the LangSmith Python and TypeScript SDKs to manage agent
repos and skill repos in the Context Hub programmatically: pull a
context for use at runtime, push a new commit, list and search repos,
check existence, and delete repos.
Install packages
If you already have LANGSMITH_API_KEY set for your workspace, you can
skip this step. Otherwise, create one in Settings > API Keys > Create
API Key in LangSmith.
export LANGSMITH_API_KEY="lsv2_..."
Identifiers
Most methods accept an identifier string that names a context repo.
The accepted forms are:
name — resolves against the current workspace owner.
owner/name — fully qualified.
owner/name:version — pinned to a specific commit hash or tag.
The optional version argument on pull methods overrides any version
embedded in the identifier. If no version is provided, the latest commit
is returned.
Schemas
Contexts are made up of typed entries. An entry is either a file with
inline content or a link to another agent or skill repo.
from langsmith.schemas import (
AgentContext,
AgentEntry,
Entry,
FileEntry,
SkillContext,
SkillEntry,
)
# A file with inline content.
file = FileEntry(content="You are a helpful assistant.")
# A link to another agent repo (optionally pinned to a commit).
agent_link = AgentEntry(repo_handle="email-assistant")
# A link to a skill repo (optionally pinned to a commit).
skill_link = SkillEntry(repo_handle="deep-research")
AgentContext and SkillContext are the response shapes returned when
you pull a repo. Both contain:
| Field | Type | Description |
|---|
owner | string | The handle of the repo owner. |
repo | string | The repo name. |
commit_id | UUID / string | The unique ID of the commit. |
commit_hash | string | The content-addressable commit hash. |
files | dict[str, Entry] | The files in the commit, keyed by their path. |
Push an agent
Create a new agent repo or commit a new version of an existing one. If
the repo doesn’t exist yet, it is created with the metadata you provide
(description, readme, tags, is_public); if it already exists,
those fields are patched only when explicitly passed.
The method returns a URL pointing to the new commit in the LangSmith UI.
from langsmith import Client
from langsmith.schemas import FileEntry
client = Client()
url = client.context.push_agent(
"email-assistant",
files={
"AGENTS.md": FileEntry(
content="You are an email triage assistant.",
),
"tools.json": FileEntry(content='{"tools": []}'),
},
description="Triages and drafts replies to incoming email.",
tags=["email", "productivity"],
is_public=False,
)
print(url)
Parameters
| Parameter | Type | Description |
|---|
identifier | string | The agent’s identifier. |
files | dict[str, Entry | None] | Map of file path to Entry. Pass None / null to delete a path in this commit. |
parent_commit / parentCommit | string (optional) | Parent commit hash for optimistic concurrency. Must be 8–64 hex characters when provided. |
description | string (optional) | Repo description; set on creation or patched on update. |
readme | string (optional) | Repo readme content. |
tags | string[] (optional) | Repo tags. |
is_public / isPublic | boolean (optional) | Whether the repo is publicly discoverable. |
Push a skill
Identical surface to push_agent, but commits to a skill repo. Use
this for reusable capabilities that other agents can depend on.
from langsmith import Client
from langsmith.schemas import FileEntry
client = Client()
url = client.context.push_skill(
"deep-research",
files={
"SKILL.md": FileEntry(content="Conduct deep multi-step research."),
},
description="Multi-step web research with citations.",
tags=["research"],
)
print(url)
Pull an agent
Pull a snapshot of an agent repo. By default the latest commit is
returned; pass a commit hash or tag via version (or embed it in the
identifier as owner/name:version) to pull a specific version.
from langsmith import Client
client = Client()
agent = client.context.pull_agent("email-assistant")
print(agent.commit_hash)
print(list(agent.files))
# Pull a specific commit.
pinned = client.context.pull_agent("email-assistant", version="7ca95573")
# Pull a tagged commit (for example, the production tag).
prod = client.context.pull_agent("email-assistant:production")
Parameters
| Parameter | Type | Description |
|---|
identifier | string | The agent’s identifier. May include an inline version: owner/name:version. |
version | string (optional) | Commit hash or tag to pull. Overrides any version embedded in the identifier. |
Returns an AgentContext.
Pull a skill
Same shape as pull_agent, returning a SkillContext.
from langsmith import Client
client = Client()
skill = client.context.pull_skill("deep-research")
print(skill.files["SKILL.md"].content)
Check whether a repo exists
Use these methods to check whether an agent or skill repo exists in your
workspace before pushing or pulling.
from langsmith import Client
client = Client()
if client.context.agent_exists("email-assistant"):
print("agent already exists")
if not client.context.skill_exists("deep-research"):
print("skill not found")
List agents and skills
List repos of either type, with optional filters for visibility,
archived state, and a search query.
from langsmith import Client
client = Client()
# Python returns a paginated response.
result = client.context.list_agents(limit=20, query="email")
for repo in result.repos:
print(repo.repo_handle)
skills = client.context.list_skills(is_public=True)
Parameters
| Parameter | Type | Description |
|---|
limit | int (Python only) | Maximum number of repos to return per page. Defaults to 100. |
offset | int (Python only) | Number of repos to skip. Defaults to 0. |
is_public / isPublic | boolean (optional) | Filter to only public (or only private) repos. |
is_archived / isArchived | boolean (optional) | Filter by archived state. Defaults to False. |
query | string (optional) | Search query matched as a prefix against the repo handle. |
Python’s list_agents / list_skills return a ListPromptsResponse with explicit
limit and offset for manual pagination. TypeScript’s listAgents / listSkills
return an AsyncIterableIterator that handles pagination automatically as you
consume it.
Delete an agent or skill
Delete a repo and all its owned child file repos. This operation cannot
be undone.
from langsmith import Client
client = Client()
client.context.delete_agent("email-assistant")
client.context.delete_skill("deep-research")
Async usage (Python)
Every method on client.context has an async equivalent on
AsyncClient.context. The signatures and return types are identical;
only the call site differs.
from langsmith import AsyncClient
client = AsyncClient()
agent = await client.context.pull_agent("email-assistant")
url = await client.context.push_agent(
"email-assistant",
files={"AGENTS.md": FileEntry(content="...")},
)
The TypeScript SDK is async by default; there is no separate async
client.
Errors
| Error | When it’s raised |
|---|
LangSmithUserError | Invalid repo_handle, malformed parent_commit, or other client-side validation. |
LangSmithNotFoundError | Pulling a repo or commit that does not exist. |
LangSmithConflictError | Concurrent commit collision when parent_commit does not match the latest commit. |
HTTPError | Any other server-side failure during the request. |
TypeScript surfaces these as standard Error instances. Use the
provided isLangSmithNotFoundError / isLangSmithConflictError
helpers to discriminate.