chore: sync skills from openai/plugins

This commit is contained in:
github-actions[bot] committed 2026-08-14 04:21:54 +00:00
1 parent 571a645c44
commit 49183cac0a
18 files changed
+1917 -244

No files matched your search

+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf of
any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don\'t include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+147 -166
View File
@@ -1,174 +1,155 @@
---
name: agents-sdk
description: Build, run, deploy, and evaluate OpenAI Agents SDK apps from Codex. Use when the user asks to create or adapt an Agents SDK app, build from a prompt or Codex thread, prepare a runnable agent prototype, add a focused eval harness, or deploy locally through the Agents SDK Deployment Manager.
description: Build AI agents on Cloudflare Workers using the Agents SDK. Load when creating stateful agents, durable workflows, real-time WebSocket apps, scheduled tasks, MCP servers, or chat applications. Covers Agent class, state management, callable RPC, Workflows integration, and React hooks. Biases towards retrieval from Cloudflare docs over pre-trained knowledge.
---
# Agents SDK
# Cloudflare Agents SDK
Use this skill to turn an idea, repo, or prior Codex thread into a small runnable Agents SDK app. Verify it locally, then deploy it through the local Deployment Manager when the user wants a running service. Prefer Python unless the user asks for TypeScript.
Your knowledge of the Agents SDK may be outdated. **Prefer retrieval over pre-training** for any Agents SDK task.
## Retrieval Sources
Fetch current docs from `https://github.com/cloudflare/agents/tree/main/docs` before implementing.
| Topic | Doc | Use for |
|-------|-----|---------|
| Getting started | `docs/getting-started.md` | First agent, project setup |
| State | `docs/state.md` | `setState`, `validateStateChange`, persistence |
| Routing | `docs/routing.md` | URL patterns, `routeAgentRequest`, `basePath` |
| Callable methods | `docs/callable-methods.md` | `@callable`, RPC, streaming, timeouts |
| Scheduling | `docs/scheduling.md` | `schedule()`, `scheduleEvery()`, cron |
| Workflows | `docs/workflows.md` | `AgentWorkflow`, durable multi-step tasks |
| HTTP/WebSockets | `docs/http-websockets.md` | Lifecycle hooks, hibernation |
| Email | `docs/email.md` | Email routing, secure reply resolver |
| MCP client | `docs/mcp-client.md` | Connecting to MCP servers |
| MCP server | `docs/mcp-servers.md` | Building MCP servers with `McpAgent` |
| Client SDK | `docs/client-sdk.md` | `useAgent`, `useAgentChat`, React hooks |
| Human-in-the-loop | `docs/human-in-the-loop.md` | Approval flows, pausing workflows |
| Resumable streaming | `docs/resumable-streaming.md` | Stream recovery on disconnect |
Cloudflare docs: https://developers.cloudflare.com/agents/
## Capabilities
The Agents SDK provides:
- **Persistent state** - SQLite-backed, auto-synced to clients
- **Callable RPC** - `@callable()` methods invoked over WebSocket
- **Scheduling** - One-time, recurring (`scheduleEvery`), and cron tasks
- **Workflows** - Durable multi-step background processing via `AgentWorkflow`
- **MCP integration** - Connect to MCP servers or build your own with `McpAgent`
- **Email handling** - Receive and reply to emails with secure routing
- **Streaming chat** - `AIChatAgent` with resumable streams
- **React hooks** - `useAgent`, `useAgentChat` for client apps
## FIRST: Verify Installation
```bash
npm ls agents # Should show agents package
```
If not installed:
```bash
npm install agents
```
## Wrangler Configuration
```jsonc
{
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }]
}
```
## Agent Class
```typescript
import { Agent, routeAgentRequest, callable } from "agents";
type State = { count: number };
export class Counter extends Agent<Env, State> {
initialState = { count: 0 };
// Validation hook - runs before state persists (sync, throwing rejects the update)
validateStateChange(nextState: State, source: Connection | "server") {
if (nextState.count < 0) throw new Error("Count cannot be negative");
}
// Notification hook - runs after state persists (async, non-blocking)
onStateUpdate(state: State, source: Connection | "server") {
console.log("State updated:", state);
}
@callable()
increment() {
this.setState({ count: this.state.count + 1 });
return this.state.count;
}
}
export default {
fetch: (req, env) => routeAgentRequest(req, env) ?? new Response("Not found", { status: 404 })
};
```
## Routing
Requests route to `/agents/{agent-name}/{instance-name}`:
| Class | URL |
|-------|-----|
| `Counter` | `/agents/counter/user-123` |
| `ChatRoom` | `/agents/chat-room/lobby` |
Client: `useAgent({ agent: "Counter", name: "user-123" })`
## Core APIs
| Task | API |
|------|-----|
| Read state | `this.state.count` |
| Write state | `this.setState({ count: 1 })` |
| SQL query | `` this.sql`SELECT * FROM users WHERE id = ${id}` `` |
| Schedule (delay) | `await this.schedule(60, "task", payload)` |
| Schedule (cron) | `await this.schedule("0 * * * *", "task", payload)` |
| Schedule (interval) | `await this.scheduleEvery(30, "poll")` |
| RPC method | `@callable() myMethod() { ... }` |
| Streaming RPC | `@callable({ streaming: true }) stream(res) { ... }` |
| Start workflow | `await this.runWorkflow("ProcessingWorkflow", params)` |
## React Client
```tsx
import { useAgent } from "agents/react";
function App() {
const [state, setLocalState] = useState({ count: 0 });
const agent = useAgent({
agent: "Counter",
name: "my-instance",
onStateUpdate: (newState) => setLocalState(newState),
onIdentity: (name, agentType) => console.log(`Connected to ${name}`)
});
return (
<button onClick={() => agent.setState({ count: state.count + 1 })}>
Count: {state.count}
</button>
);
}
```
## References
- Agents guide: https://developers.openai.com/api/docs/guides/agents
- Sandbox Agents guide: https://developers.openai.com/api/docs/guides/agents/sandboxes
- Python SDK: https://github.com/openai/openai-agents-python
- TypeScript SDK: https://github.com/openai/openai-agents-js
- Deployment Manager: https://github.com/openai/openai-cookbook/tree/main/examples/agents_sdk/deployment_manager
- Agent evals guide: https://developers.openai.com/api/docs/guides/agent-evals
## Rules
- Read the Agents guide before creating or changing an Agents SDK implementation.
- Read the Sandbox Agents guide before choosing `SandboxAgent`, workspace manifests, shell/file access, skills, or sandbox backend behavior.
- If a docs MCP/tool is unavailable, read the official docs URLs directly instead of skipping the docs gate.
- Follow current docs for SDK semantics and the target repo for packaging, naming, and command conventions.
- Keep generated app files, eval files, and deployment-generated files separate in the final summary.
## Intake
Classify the request before editing:
- New app from prompt or idea: build the smallest runnable Agents SDK app that proves the workflow.
- Existing app or demo: inspect the repo and add the smallest Agents SDK layer needed to make the workflow agentic.
- Prior Codex work: turn thread IDs, session links, rollout JSONL paths, or pasted summaries into a short build brief before writing code.
- Evals request: add a focused local eval harness against the real agent path.
- Deployment-only request: deploy the existing app without rebuilding unless deployment reveals a small required fix.
## API Access
Agents SDK apps need `OPENAI_API_KEY` to run. Before building, running, or testing an app that calls the OpenAI API, use the `openai-platform-api-key` skill in this plugin as the credential gate. Follow that skill's confirmation flow and never print, summarize, or commit secret values.
## Build Workflow
1. Inspect the target repo.
Read `README.md`, dependency files, app entrypoints, existing examples, and any domain-specific `skills/` or policy files.
2. Define the app contract.
Capture the agent goal, input shape, expected output, tools, state, approval gates, and the local command that proves the workflow.
3. Set up dependencies using the repo's existing package manager.
For Python projects, prefer `uv`; add `openai-agents` when the project owns dependencies.
4. Start with one agent.
Use a single `Agent` with clear static `instructions` and `Runner.run` until the workflow proves it needs specialists, handoffs, structured outputs, or sandbox execution.
5. Add tools deliberately.
Use `@function_tool` for deterministic local actions such as lookups, calculations, file transforms, API calls, or validation. Keep side effects narrow and schemas explicit.
6. Add sandbox only for workspace tasks.
Use `SandboxAgent` when the agent must inspect files, run shell commands, use workspace skills, or create artifacts in an isolated environment. Keep ordinary business workflows on normal `Agent` plus tools.
7. Make it runnable.
Provide a local smoke command, sample input, and expected observable output. If there is a UI, wire it to the agent path and verify the core workflow, not just rendering.
For HTTP apps that may be deployed, make `uv run python main.py` start the web service when `PORT` is present, keep CLI-only smoke behavior behind explicit arguments or the no-`PORT` path, and expose `/health` for readiness.
For every new prototype or substantial app build, prefer:
```text
<project>/
agent.py # Agent definition, tools, and run helper
main.py # API/server/CLI entrypoint if needed
pyproject.toml # includes openai-agents if the project owns deps
docs/
prompt.md # runtime prompt or instructions used by the app agent
agent-interactions.png
agent-sequence.png
data/ # small sample inputs or fixtures
skills/<domain>/ # optional domain instructions or reusable policy
README.md # local run instructions if the project already uses READMEs
```
Generate diagrams directly as PNG files. Do not create SVG diagram sources or rely on browser screenshots of SVGs unless the user explicitly asks for editable vector sources. For one-off diagram generation, prefer a small script under `scripts/generate_diagrams.py` and run extra drawing dependencies with `uv run --no-project --with ...` so the app dependency file stays focused.
## Build From Codex
When the source is prior Codex work, create a compact brief before building:
- confirmed facts from the source threads;
- inferences and open questions;
- app goal, agent behavior, tools, state, UI, approvals, sandbox needs, and deployment assumptions;
- a standalone build prompt that can drive the implementation.
Prefer the newest user direction when threads conflict. Keep secrets out of the brief and mention missing environment variable names only.
If the user already asked to build after planning, continue from the brief into the build workflow. Otherwise ask for approval before implementing.
## Eval Workflow
Add evals when requested. Default to a local harness that exercises the real agent workflow rather than a mock or contract-only path.
Before creating or changing evals, read the Agents guide and Agent evals guide. Read trace grading, evals, and graders docs before generating platform eval configs, grader JSON, or dataset upload scripts.
Prefer an `evals/` folder unless the repo already has a stronger convention:
```text
<project>/
evals/
README.md
cases.jsonl
graders.py
run_local.py
results/
.gitignore
```
Design a small case matrix around meaningful behavior: happy path, missing evidence, escalation boundary, required or forbidden tool calls, approval gates, state updates, and regressions from observed bugs. Grade behavior such as structured output, tool calls, handoffs, guardrails, trace IDs, event logs, state changes, and approval behavior instead of volatile IDs or exact prose unless wording is contractual.
`evals/run_local.py` should load cases, add the app root to `sys.path`, run each case through the app's real agent path, isolate or reset state, require needed environment variable names up front, write `evals/results/latest.json`, and exit non-zero on failures.
## Deploy Workflow
Use the Deployment Manager from `openai-cookbook` for local deployments. Default to `local-docker` unless the user or app requires a different local target.
1. Check deployable app signals:
- an app/orchestrator entrypoint, usually `main.py`;
- dependency metadata, preferably `pyproject.toml`;
- `openai-agents` in the app dependencies;
- `PORT` support for local app startup, with `uv run python main.py` starting the HTTP service when `PORT` is set;
- `/health` readiness endpoint;
- optional `SANDBOX_BACKEND` support for sandbox-backed apps;
- optional `docs/prompt.md`, `docs/agent-interactions.png`, and `docs/agent-sequence.png` for manager app details.
2. Resolve the manager directory.
Prefer `DEPLOYMENT_MANAGER_ROOT` when set. Otherwise use `$HOME/code/openai-cookbook/examples/agents_sdk/deployment_manager`. If the cookbook checkout is missing, clone `https://github.com/openai/openai-cookbook`. If it exists, update it with `git pull --ff-only` unless the user asked to avoid updating local checkouts. Stop and report local changes or diverged history instead of forcing the checkout. Verify `$MANAGER_DIR/Makefile` exists before deploying.
3. Deploy through the manager:
```bash
make -C "$MANAGER_DIR" deploy PROJECT_PATH=<absolute-app-path>
```
Useful options:
```bash
make -C "$MANAGER_DIR" deploy PROJECT_PATH=/path/to/app APP_PORT=8421
make -C "$MANAGER_DIR" deploy PROJECT_PATH=/path/to/app TARGET=local-process
make -C "$MANAGER_DIR" deploy PROJECT_PATH=/path/to/app SANDBOX_BACKEND=docker
make -C "$MANAGER_DIR" start
make -C "$MANAGER_DIR" health
```
4. Let the manager own extraction and deployment records.
The helper imports the project, creates or reuses a matching deployment, starts it, and prints JSON with `manager_url`, `deployment`, and `app_url`. For `local-docker`, it may generate or reuse an app-level `Dockerfile`. If that changes the app worktree, report it and do not revert user files.
5. Verify the result.
Check manager health, the app `/health` readiness endpoint, and deployment sessions/containers when available.
```bash
curl -fsS http://127.0.0.1:8732/api/health
curl -fsS <app-url>/health
curl -fsS http://127.0.0.1:8732/api/deployments/<deployment-id>/sessions
curl -fsS http://127.0.0.1:8732/api/deployments/<deployment-id>/containers
```
Run `git -C <app-path> status --short` when the app path is inside a git checkout so generated Dockerfiles or other local edits are visible.
## Done Criteria
Before handing back:
- the app has a clear local run command and smoke result, or a clear blocker;
- deployment was attempted when requested and the manager/app URLs are reported;
- any missing credentials, model access, port conflicts, Docker issues, or layout warnings are explicit;
- generated app files, eval files, and deployment-generated files are separated in the summary.
- **[references/workflows.md](references/workflows.md)** - Durable Workflows integration
- **[references/callable.md](references/callable.md)** - RPC methods, streaming, timeouts
- **[references/state-scheduling.md](references/state-scheduling.md)** - State persistence, scheduling
- **[references/streaming-chat.md](references/streaming-chat.md)** - AIChatAgent, resumable streams
- **[references/mcp.md](references/mcp.md)** - MCP server integration
- **[references/email.md](references/email.md)** - Email routing and handling
- **[references/codemode.md](references/codemode.md)** - Code Mode (experimental)
+4 -1
View File
@@ -1,3 +1,6 @@
interface:
display_name: "Agents SDK"
short_description: "Build and deploy OpenAI Agents SDK apps"
short_description: "Build stateful agents on Cloudflare Workers"
icon_small: "./assets/cloudflare-small.svg"
icon_large: "./assets/cloudflare.png"
default_prompt: "Use the Cloudflare Agents SDK to design or implement this agent on Workers with the right state, routing, and workflow patterns."
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path fill="currentColor" d="M16.5088 16.8447c.1475-.5068.0908-.9707-.1553-1.3154-.2246-.3164-.6045-.499-1.0615-.5205l-8.6592-.1123a.1559.1559 0 0 1-.1333-.0713c-.0283-.042-.0351-.0986-.021-.1553.0278-.084.1123-.1484.2036-.1562l8.7359-.1123c1.0351-.0489 2.1601-.8868 2.5537-1.9136l.499-1.3013c.0215-.0561.0293-.1128.0147-.168-.5625-2.5463-2.835-4.4453-5.5499-4.4453-2.5039 0-4.6284 1.6177-5.3876 3.8614-.4927-.3658-1.1187-.5625-1.794-.499-1.2026.119-2.1665 1.083-2.2861 2.2856-.0283.31-.0069.6128.0635.894C1.5683 13.171 0 14.7754 0 16.752c0 .1748.0142.3515.0352.5273.0141.083.0844.1475.1689.1475h15.9814c.0909 0 .1758-.0645.2032-.1553l.12-.4268zm2.7568-5.5634c-.0771 0-.1611 0-.2383.0112-.0566 0-.1054.0415-.127.0976l-.3378 1.1744c-.1475.5068-.0918.9707.1543 1.3164.2256.3164.6055.498 1.0625.5195l1.8437.1133c.0557 0 .1055.0263.1329.0703.0283.043.0351.1074.0214.1562-.0283.084-.1132.1485-.204.1553l-1.921.1123c-1.041.0488-2.1582.8867-2.5527 1.914l-.1406.3585c-.0283.0713.0215.1416.0986.1416h6.5977c.0771 0 .1474-.0489.169-.126.1122-.4082.1757-.837.1757-1.2803 0-2.6025-2.125-4.727-4.7344-4.727"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

+92
View File
@@ -0,0 +1,92 @@
# Callable Methods
Fetch `docs/callable-methods.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
## Overview
`@callable()` exposes agent methods to clients via WebSocket RPC.
```typescript
import { Agent, callable } from "agents";
export class MyAgent extends Agent<Env, State> {
@callable()
async greet(name: string): Promise<string> {
return `Hello, ${name}!`;
}
@callable()
async processData(data: unknown): Promise<Result> {
// Long-running work
return result;
}
}
```
## Client Usage
```typescript
// Basic call
const greeting = await agent.call("greet", ["World"]);
// With timeout
const result = await agent.call("processData", [data], {
timeout: 5000 // 5 second timeout
});
```
## Streaming Responses
```typescript
import { Agent, callable, StreamingResponse } from "agents";
export class MyAgent extends Agent<Env, State> {
@callable({ streaming: true })
async streamResults(stream: StreamingResponse, query: string) {
for await (const item of fetchResults(query)) {
stream.send(JSON.stringify(item));
}
stream.close();
}
@callable({ streaming: true })
async streamWithError(stream: StreamingResponse) {
try {
// ... work
} catch (error) {
stream.error(error.message); // Signal error to client
return;
}
stream.close();
}
}
```
Client with streaming:
```typescript
await agent.call("streamResults", ["search term"], {
stream: {
onChunk: (data) => console.log("Chunk:", data),
onDone: () => console.log("Complete"),
onError: (error) => console.error("Error:", error)
}
});
```
## Introspection
```typescript
// Get list of callable methods on an agent
const methods = await agent.call("getCallableMethods", []);
// Returns: ["greet", "processData", "streamResults", ...]
```
## When to Use
| Scenario | Use |
|----------|-----|
| Browser/mobile calling agent | `@callable()` |
| External service calling agent | `@callable()` |
| Worker calling agent (same codebase) | DO RPC directly |
| Agent calling another agent | `getAgentByName()` + DO RPC |
+207
View File
@@ -0,0 +1,207 @@
# Code Mode (Experimental)
Code Mode generates executable JavaScript instead of making individual tool calls. This significantly reduces token usage and enables complex multi-tool workflows.
## Why Code Mode?
Traditional tool calling:
- One tool call per LLM request
- Multiple round-trips for chained operations
- High token usage for complex workflows
Code Mode:
- LLM generates code that orchestrates multiple tools
- Single execution for complex workflows
- Self-debugging and error recovery
- Ideal for MCP server orchestration
## Setup
### 1. Wrangler Config
```jsonc
{
"name": "my-agent-worker",
"compatibility_flags": ["experimental", "enable_ctx_exports"],
"durable_objects": {
// "class_name" must match your Agent class name exactly
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [
// Required: list all Agent classes for SQLite storage
{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }
],
"services": [
{
"binding": "globalOutbound",
// "service" must match "name" above
"service": "my-agent-worker",
"entrypoint": "globalOutbound"
},
{
"binding": "CodeModeProxy",
"service": "my-agent-worker",
"entrypoint": "CodeModeProxy"
}
],
"worker_loaders": [{ "binding": "LOADER" }]
}
```
### 2. Export Required Classes
```typescript
// Export the proxy for tool execution (required for codemode)
export { CodeModeProxy } from "@cloudflare/codemode/ai";
// Define outbound fetch handler for security filtering
export const globalOutbound = {
fetch: async (input: string | URL | RequestInfo, init?: RequestInit) => {
const url = new URL(
typeof input === "string"
? input
: typeof input === "object" && "url" in input
? input.url
: input.toString()
);
// Block certain domains if needed
if (url.hostname === "blocked.example.com") {
return new Response("Not allowed", { status: 403 });
}
return fetch(input, init);
}
};
```
### 3. Install Dependencies
```bash
npm install @cloudflare/codemode ai @ai-sdk/openai zod
```
### 4. Use Code Mode in Agent
```typescript
import { Agent } from "agents";
import { experimental_codemode as codemode } from "@cloudflare/codemode/ai";
import { streamText, tool, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";
import { env } from "cloudflare:workers";
import { z } from "zod";
const tools = {
getWeather: tool({
description: "Get weather for a location",
parameters: z.object({ location: z.string() }),
execute: async ({ location }) => `Weather: ${location} 72°F`
}),
sendEmail: tool({
description: "Send an email",
parameters: z.object({ to: z.string(), subject: z.string(), body: z.string() }),
execute: async ({ to, subject, body }) => `Email sent to ${to}`
})
};
export class MyAgent extends Agent<Env, State> {
tools = {};
// Method called by codemode proxy
callTool(functionName: string, args: unknown[]) {
return this.tools[functionName]?.execute?.(args, {
abortSignal: new AbortController().signal,
toolCallId: "codemode",
messages: []
});
}
async onChatMessage() {
this.tools = { ...tools, ...this.mcp.getAITools() };
const { prompt, tools: wrappedTools } = await codemode({
prompt: "You are a helpful assistant...",
tools: this.tools,
globalOutbound: env.globalOutbound,
loader: env.LOADER,
proxy: this.ctx.exports.CodeModeProxy({
props: {
binding: "MyAgent", // Class name
name: this.name, // Instance name
callback: "callTool" // Method to call
}
})
});
const result = streamText({
system: prompt,
model: openai("gpt-4o"),
messages: await convertToModelMessages(this.state.messages),
tools: wrappedTools // Use wrapped tools, not original
});
// ... handle stream
}
}
```
## Generated Code Example
When user asks "Check the weather in NYC and email me the forecast", codemode generates:
```javascript
async function executeTask() {
const weather = await codemode.getWeather({ location: "NYC" });
await codemode.sendEmail({
to: "user@example.com",
subject: "NYC Weather Forecast",
body: `Current weather: ${weather}`
});
return { success: true, weather };
}
```
## MCP Server Orchestration
Code Mode excels at orchestrating multiple MCP servers:
```javascript
async function executeTask() {
// Query file system MCP
const files = await codemode.listFiles({ path: "/projects" });
// Query database MCP
const status = await codemode.queryDatabase({
query: "SELECT * FROM projects WHERE name = ?",
params: [files[0].name]
});
// Conditional logic based on results
if (status.length === 0) {
await codemode.createTask({
title: `Review: ${files[0].name}`,
priority: "high"
});
}
return { files, status };
}
```
## When to Use
| Scenario | Use Code Mode? |
|----------|---------------|
| Single tool call | No |
| Chained tool calls | Yes |
| Conditional logic across tools | Yes |
| MCP multi-server workflows | Yes |
| Token budget constrained | Yes |
| Simple Q&A chat | No |
## Limitations
- Experimental - API may change
- Requires Cloudflare Workers
- JavaScript execution only (Python planned)
- Requires additional wrangler config
+146
View File
@@ -0,0 +1,146 @@
# Email Handling
Fetch `docs/email.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
## Overview
Agents receive and reply to emails via Cloudflare Email Routing.
## Wrangler Configuration
```jsonc
{
"durable_objects": {
"bindings": [{ "name": "EmailAgent", "class_name": "EmailAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["EmailAgent"] }],
"send_email": [
{ "name": "SEB", "destination_address": "reply@yourdomain.com" }
]
}
```
## Basic Email Handler
```typescript
import { Agent } from "agents";
import { type AgentEmail } from "agents/email";
import PostalMime from "postal-mime";
export class EmailAgent extends Agent<Env, State> {
async onEmail(email: AgentEmail) {
const raw = await email.getRaw();
const parsed = await PostalMime.parse(raw);
console.log("From:", email.from);
console.log("Subject:", parsed.subject);
await this.replyToEmail(email, {
fromName: "My Agent",
subject: `Re: ${parsed.subject}`,
body: "Thanks for your email!"
});
}
}
```
## Routing Emails
```typescript
import { routeAgentRequest, routeAgentEmail } from "agents";
import { createAddressBasedEmailResolver } from "agents/email";
export default {
async email(message, env) {
await routeAgentEmail(message, env, {
resolver: createAddressBasedEmailResolver("EmailAgent")
});
},
async fetch(request, env) {
return routeAgentRequest(request, env) ?? new Response("Not found", { status: 404 });
}
};
```
## Resolvers
### Address-Based (Inbound Mail)
Routes based on recipient address:
```typescript
import { createAddressBasedEmailResolver } from "agents/email";
const resolver = createAddressBasedEmailResolver("EmailAgent");
// support@example.com → EmailAgent, instance "support"
// NotificationAgent+user123@example.com → NotificationAgent, instance "user123"
```
### Secure Reply (Reply Flows)
Verifies replies are authentic using HMAC-SHA256 signatures:
```typescript
import { createSecureReplyEmailResolver } from "agents/email";
const resolver = createSecureReplyEmailResolver(env.EMAIL_SECRET, {
maxAge: 7 * 24 * 60 * 60, // 7 days (default: 30 days)
onInvalidSignature: (email, reason) => {
console.warn(`Invalid signature from ${email.from}: ${reason}`);
}
});
```
Sign outbound emails to enable secure reply routing:
```typescript
await this.replyToEmail(email, {
fromName: "My Agent",
body: "Thanks!",
secret: this.env.EMAIL_SECRET // Signs headers for secure reply routing
});
```
### Catch-All (Single Instance)
Routes all emails to one agent instance:
```typescript
import { createCatchAllEmailResolver } from "agents/email";
const resolver = createCatchAllEmailResolver("EmailAgent", "default");
```
### Combining Resolvers
```typescript
async email(message, env) {
const secureReply = createSecureReplyEmailResolver(env.EMAIL_SECRET);
const addressBased = createAddressBasedEmailResolver("EmailAgent");
await routeAgentEmail(message, env, {
resolver: async (email, env) => {
// Try secure reply first
const result = await secureReply(email, env);
if (result) return result;
// Fall back to address-based
return addressBased(email, env);
}
});
}
```
## Utilities
```typescript
import { isAutoReplyEmail } from "agents/email";
async onEmail(email: AgentEmail) {
if (isAutoReplyEmail(email.headers)) {
// Skip auto-replies (vacation, out-of-office, etc.)
return;
}
// Process email...
}
```
+154
View File
@@ -0,0 +1,154 @@
# MCP Server Integration
Fetch `docs/mcp-client.md` and `docs/mcp-servers.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
Agents include a multi-server MCP client for connecting to external MCP servers.
## Add an MCP Server
```typescript
import { Agent, callable } from "agents";
export class MyAgent extends Agent<Env, State> {
@callable()
async addServer(name: string, url: string) {
// Options-based API (recommended)
const result = await this.addMcpServer(name, url, {
callbackHost: "https://my-worker.workers.dev",
transport: { headers: { Authorization: "Bearer ..." } }
});
if (result.state === "authenticating") {
// OAuth required - redirect user to result.authUrl
return { needsAuth: true, authUrl: result.authUrl };
}
return { ready: true, id: result.id };
}
}
```
## Use MCP Tools
```typescript
async onChatMessage() {
// Get AI-compatible tools from all connected MCP servers
const mcpTools = this.mcp.getAITools();
const allTools = {
...localTools,
...mcpTools
};
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(this.messages),
tools: allTools
});
return result.toUIMessageStreamResponse();
}
```
## List MCP Resources
```typescript
// List all registered servers
const servers = this.mcp.listServers();
// List tools from all servers
const tools = this.mcp.listTools();
// List resources
const resources = this.mcp.listResources();
// List prompts
const prompts = this.mcp.listPrompts();
```
## Remove Server
```typescript
await this.removeMcpServer(serverId);
```
## Building an MCP Server
Use `McpAgent` from the SDK to create an MCP server.
**Install dependencies:**
```bash
npm install @modelcontextprotocol/sdk zod
```
**Wrangler config:**
```jsonc
{
"durable_objects": {
"bindings": [{ "name": "MyMCP", "class_name": "MyMCP" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyMCP"] }]
}
```
**Server implementation:**
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { McpAgent } from "agents/mcp";
import { z } from "zod";
type State = { counter: number };
export class MyMCP extends McpAgent<Env, State, {}> {
server = new McpServer({
name: "MyMCPServer",
version: "1.0.0"
});
initialState = { counter: 0 };
async init() {
// Register a resource
this.server.resource("counter", "mcp://resource/counter", (uri) => ({
contents: [{ text: String(this.state.counter), uri: uri.href }]
}));
// Register a tool
this.server.registerTool(
"increment",
{
description: "Increment the counter",
inputSchema: { amount: z.number().default(1) }
},
async ({ amount }) => {
this.setState({ counter: this.state.counter + amount });
return {
content: [{ text: `Counter: ${this.state.counter}`, type: "text" }]
};
}
);
}
}
```
## Serve MCP Server
```typescript
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
// SSE transport (legacy)
if (url.pathname.startsWith("/sse")) {
return MyMCP.serveSSE("/sse", { binding: "MyMCP" }).fetch(request, env, ctx);
}
// Streamable HTTP transport (recommended)
if (url.pathname.startsWith("/mcp")) {
return MyMCP.serve("/mcp", { binding: "MyMCP" }).fetch(request, env, ctx);
}
return new Response("Not found", { status: 404 });
}
};
```
@@ -0,0 +1,164 @@
# State & Scheduling
Fetch `docs/state.md` and `docs/scheduling.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
## State Management
State persists to SQLite and broadcasts to connected clients automatically.
### Define Typed State
```typescript
type State = {
count: number;
items: string[];
};
export class MyAgent extends Agent<Env, State> {
initialState: State = { count: 0, items: [] };
}
```
### Read and Update
```typescript
// Read (lazy-loaded from SQLite)
const count = this.state.count;
// Write (sync, persists, broadcasts)
this.setState({ count: this.state.count + 1 });
```
### Validation Hook
`validateStateChange()` runs synchronously before state persists. Throw to reject the update.
```typescript
validateStateChange(nextState: State, source: Connection | "server") {
if (nextState.count < 0) {
throw new Error("Count cannot be negative");
}
}
```
### Execution Order
1. `validateStateChange(nextState, source)` - sync, gating
2. State persisted to SQLite
3. State broadcast to connected clients
4. `onStateUpdate(nextState, source)` - async via `ctx.waitUntil`, non-gating
### Client-Side Sync (React)
```tsx
import { useAgent } from "agents/react";
function App() {
const [state, setLocalState] = useState<State>({ count: 0 });
const agent = useAgent<State>({
agent: "MyAgent",
name: "instance-1",
onStateUpdate: (newState) => setLocalState(newState)
});
return <button onClick={() => agent.setState({ count: state.count + 1 })}>
Count: {state.count}
</button>;
}
```
## SQL API
Direct SQLite access for custom queries:
```typescript
// Create table
this.sql`
CREATE TABLE IF NOT EXISTS items (
id TEXT PRIMARY KEY,
name TEXT,
created_at INTEGER DEFAULT (unixepoch())
)
`;
// Insert
this.sql`INSERT INTO items (id, name) VALUES (${id}, ${name})`;
// Query with types
const items = this.sql<{ id: string; name: string }>`
SELECT * FROM items WHERE name LIKE ${`%${search}%`}
`;
```
## Scheduling
### Schedule Types
| Mode | Syntax | Use Case |
|------|--------|----------|
| Delay | `this.schedule(60, ...)` | Run in 60 seconds |
| Date | `this.schedule(new Date(...), ...)` | Run at specific time |
| Cron | `this.schedule("0 8 * * *", ...)` | Recurring schedule |
| Interval | `this.scheduleEvery(30, ...)` | Fixed interval (every 30s) |
### Examples
```typescript
// Delay (seconds)
await this.schedule(60, "checkStatus", { id: "abc123" });
// Specific date
await this.schedule(new Date("2025-12-25T00:00:00Z"), "sendGreeting", { to: "user" });
// Cron (recurring)
await this.schedule("0 9 * * 1-5", "weekdayReport", {});
// Fixed interval (every 30 seconds, overlap prevention built-in)
await this.scheduleEvery(30, "pollUpdates");
await this.scheduleEvery(300, "syncData", { source: "api" });
```
### Handler
```typescript
async sendGreeting(payload: { to: string }, schedule: Schedule) {
console.log(`Sending greeting to ${payload.to}`);
// Cron schedules auto-reschedule; one-time schedules are deleted
}
```
### Manage Schedules
```typescript
const schedules = this.getSchedules();
const crons = this.getSchedules({ type: "cron" });
await this.cancelSchedule(schedule.id);
```
## Lifecycle Callbacks
```typescript
export class MyAgent extends Agent<Env, State> {
async onStart() {
// Agent started or woke from hibernation
}
onConnect(conn: Connection, ctx: ConnectionContext) {
// WebSocket connected
}
onMessage(conn: Connection, message: WSMessage) {
// WebSocket message (non-RPC)
}
onStateUpdate(state: State, source: Connection | "server") {
// State changed (async, non-blocking)
}
onError(error: unknown) {
// Error handler
throw error; // Re-throw to propagate
}
}
```
@@ -0,0 +1,178 @@
# Streaming Chat with AIChatAgent
Fetch `docs/resumable-streaming.md` and `docs/client-sdk.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
`AIChatAgent` provides streaming chat with automatic message persistence and resumable streams.
## Basic Chat Agent
```typescript
import { AIChatAgent } from "@cloudflare/ai-chat";
import { streamText, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";
export class Chat extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(this.messages),
onFinish
});
return result.toUIMessageStreamResponse();
}
}
```
## With Custom System Prompt
```typescript
export class Chat extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
const result = streamText({
model: openai("gpt-4o"),
system: "You are a helpful assistant specializing in...",
messages: await convertToModelMessages(this.messages),
onFinish
});
return result.toUIMessageStreamResponse();
}
}
```
## With Tools
```typescript
import { tool } from "ai";
import { z } from "zod";
const tools = {
getWeather: tool({
description: "Get weather for a location",
parameters: z.object({ location: z.string() }),
execute: async ({ location }) => `Weather in ${location}: 72°F, sunny`
})
};
export class Chat extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(this.messages),
tools,
onFinish
});
return result.toUIMessageStreamResponse();
}
}
```
## Custom UI Message Stream
For more control, use `createUIMessageStream`:
```typescript
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
export class Chat extends AIChatAgent<Env> {
async onChatMessage(onFinish) {
const stream = createUIMessageStream({
execute: async ({ writer }) => {
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(this.messages),
onFinish
});
writer.merge(result.toUIMessageStream());
}
});
return createUIMessageStreamResponse({ stream });
}
}
```
## Resumable Streaming
Streams automatically resume if client disconnects and reconnects:
1. Chunks buffered to SQLite during streaming
2. On reconnect, buffered chunks sent immediately
3. Live streaming continues from where it left off
**Enabled by default.** To disable:
```tsx
const { messages } = useAgentChat({ agent, resume: false });
```
## React Client
```tsx
import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
function ChatUI() {
const agent = useAgent({
agent: "Chat",
name: "my-chat-session"
});
const {
messages,
input,
handleInputChange,
handleSubmit,
status
} = useAgentChat({ agent });
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong> {m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
disabled={status === "streaming"}
/>
<button type="submit">Send</button>
</form>
</div>
);
}
```
## Streaming RPC Methods
For non-chat streaming, use `@callable({ streaming: true })`:
```typescript
import { Agent, callable, StreamingResponse } from "agents";
export class MyAgent extends Agent<Env> {
@callable({ streaming: true })
async streamData(stream: StreamingResponse, query: string) {
for (let i = 0; i < 10; i++) {
stream.send(`Result ${i}: ${query}`);
await sleep(100);
}
stream.close();
}
}
```
Client receives streamed messages via WebSocket RPC.
## Status Values
`useAgentChat` status:
| Status | Meaning |
|--------|---------|
| `ready` | Idle, ready for input |
| `streaming` | Response streaming |
| `submitted` | Request sent, waiting |
| `error` | Error occurred |
+132
View File
@@ -0,0 +1,132 @@
# Workflows Integration
Fetch `docs/workflows.md` from `https://github.com/cloudflare/agents/tree/main/docs` for complete documentation.
## Overview
Agents handle real-time communication; Workflows handle durable execution. Together they enable:
- Long-running background tasks with automatic retries
- Human-in-the-loop approval flows
- Multi-step pipelines that survive failures
| Use Case | Recommendation |
|----------|----------------|
| Chat/messaging | Agent only |
| Quick API calls (<30s) | Agent only |
| Background processing (<30s) | Agent `queue()` |
| Long-running tasks (>30s) | Agent + Workflow |
| Human approval flows | Agent + Workflow |
## AgentWorkflow Base Class
```typescript
import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
type TaskParams = { taskId: string; data: string };
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
const params = event.payload;
// Durable step - retries on failure
const result = await step.do("process", async () => {
return processData(params.data);
});
// Non-durable: progress reporting
await this.reportProgress({ step: "process", percent: 0.5 });
// Non-durable: broadcast to connected clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
// Durable: merge state via step
await step.mergeAgentState({ lastProcessed: params.taskId });
// Durable: report completion
await step.reportComplete(result);
return result;
}
}
```
## Wrangler Configuration
```jsonc
{
"workflows": [
{ "name": "processing-workflow", "binding": "PROCESSING_WORKFLOW", "class_name": "ProcessingWorkflow" }
],
"durable_objects": {
"bindings": [{ "name": "MyAgent", "class_name": "MyAgent" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }]
}
```
## Agent Methods for Workflows
```typescript
// Start a workflow
const instance = await this.runWorkflow("ProcessingWorkflow", { taskId: "123", data: "..." });
// Send event to waiting workflow
await this.sendWorkflowEvent("ProcessingWorkflow", workflowId, { type: "approve" });
// Query workflows
const workflow = await this.getWorkflow(workflowId);
const workflows = await this.getWorkflows({ status: "running" });
// Control workflows
await this.approveWorkflow(workflowId);
await this.rejectWorkflow(workflowId);
await this.terminateWorkflow(workflowId);
await this.pauseWorkflow(workflowId);
await this.resumeWorkflow(workflowId);
// Delete workflows
await this.deleteWorkflow(workflowId);
await this.deleteWorkflows({ status: "complete", before: new Date(...) });
```
## Lifecycle Callbacks
```typescript
export class MyAgent extends Agent<Env, State> {
async onWorkflowProgress(workflowName: string, workflowId: string, progress: unknown) {
// Workflow reported progress via this.reportProgress()
this.broadcast({ type: "progress", workflowId, progress });
}
async onWorkflowComplete(workflowName: string, workflowId: string, result?: unknown) {
// Workflow finished successfully
}
async onWorkflowError(workflowName: string, workflowId: string, error: Error) {
// Workflow failed
}
async onWorkflowEvent(workflowName: string, workflowId: string, event: unknown) {
// Workflow received an event via sendWorkflowEvent()
}
}
```
## Human-in-the-Loop
```typescript
// In workflow: wait for approval
const approved = await step.waitForEvent<{ approved: boolean }>("approval", {
timeout: "7d"
});
if (!approved.approved) {
throw new Error("Rejected");
}
// From agent: approve or reject
await this.approveWorkflow(workflowId); // Sends { approved: true }
await this.rejectWorkflow(workflowId); // Sends { approved: false }
```
@@ -6,5 +6,5 @@ interface:
dependencies:
tools:
- type: "mcp"
value: "chronograph-gp"
value: "chronograph-lp"
description: "Optional Chronograph MCP server for live company metadata, investments, and metric retrieval. Not required when working from an uploaded Excel model."
+155 -43
View File
@@ -1,60 +1,172 @@
---
name: circleci-cli
description: Operate and troubleshoot CircleCI using the CircleCI CLI. Use when users ask to authenticate CLI access, inspect pipeline/workflow/job status, validate configuration locally, rerun pipelines/jobs, trigger pipelines, or gather actionable diagnostics from CLI outputs.
name: hf-cli
description: "Hugging Face Hub CLI (`hf`) for downloading, uploading, and managing repositories, models, datasets, and Spaces on the Hugging Face Hub. Replaces now deprecated `huggingface-cli` command."
---
# CircleCI CLI
Install: `curl -LsSf https://hf.co/cli/install.sh | bash -s`.
## Overview
The Hugging Face Hub CLI tool `hf` is available. IMPORTANT: The `hf` command replaces the deprecated `huggingface-cli` command.
Use this skill when the fastest path is CircleCI CLI-driven operations rather than editing config first. Prioritize safe, read-first diagnostics, then run targeted mutating commands only after confirming scope.
Use `hf --help` to view available functions. Note that auth commands are now all under `hf auth` e.g. `hf auth whoami`.
## Inputs To Gather
## Commands
- Repository path and target branch
- CircleCI project slug (if needed)
- Whether objective is inspect, rerun, trigger, or validate
- Required token/auth state and org permissions
- `hf download REPO_ID` — Download files from the Hub. `[--type CHOICE --revision TEXT --include TEXT --exclude TEXT --cache-dir TEXT --local-dir TEXT --force-download --dry-run --quiet --max-workers INTEGER]`
- `hf env` — Print information about the environment.
- `hf sync` — Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --quiet]`
- `hf upload REPO_ID` — Upload a file or a folder to the Hub. Recommended for single-commit uploads. `[--type CHOICE --revision TEXT --private --include TEXT --exclude TEXT --delete TEXT --commit-message TEXT --commit-description TEXT --create-pr --every FLOAT --quiet]`
- `hf upload-large-folder REPO_ID LOCAL_PATH` — Upload a large folder to the Hub. Recommended for resumable uploads. `[--type CHOICE --revision TEXT --private --include TEXT --exclude TEXT --num-workers INTEGER --no-report --no-bars]`
- `hf version` — Print information about the hf version.
## Workflow
### `hf auth` — Manage authentication (login, logout, etc.).
1. Verify CLI and auth state.
- Confirm `circleci` is installed and version is available.
- Confirm token/auth before issuing remote CircleCI commands.
2. Run read-only diagnostics first.
- Inspect available pipeline/project/trigger state and capture concrete identifiers.
- Extract first failing scope and step details from supported command output before rerun/trigger actions.
3. Validate config locally when relevant.
- Run config validation/processing commands before committing risky edits.
4. Run targeted mutation commands.
- Rerun only required workflow/job scope.
- Trigger pipelines with explicit parameters and branch context.
5. Report results and next action.
- Provide exact command results, remaining blockers, and safest follow-up.
- `hf auth list` — List all stored access tokens.
- `hf auth login` — Login using a token from huggingface.co/settings/tokens. `[--add-to-git-credential --force]`
- `hf auth logout` — Logout from a specific token. `[--token-name TEXT]`
- `hf auth switch` — Switch between access tokens. `[--token-name TEXT --add-to-git-credential]`
- `hf auth whoami` — Find out which huggingface.co account you are logged in as. `[--format CHOICE]`
## Guardrails
### `hf buckets` — Commands to interact with buckets.
- Prefer read-only commands before rerun/trigger/cancel operations.
- Confirm organization/project scope before mutating pipeline state.
- Never print raw secret values from environment variables or tokens.
- If permissions fail, report exact auth/scope gap and safest remediation.
- Respect installed CLI capabilities and avoid inventing commands.
- Do not use `circleci api`, `circleci workflow`, or other unavailable legacy commands unless `circleci help` confirms they exist.
- `hf buckets cp SRC` — Copy a single file to or from a bucket. `[--quiet]`
- `hf buckets create BUCKET_ID` — Create a new bucket. `[--private --exist-ok --quiet]`
- `hf buckets delete BUCKET_ID` — Delete a bucket. `[--yes --missing-ok --quiet]`
- `hf buckets info BUCKET_ID` — Get info about a bucket. `[--quiet]`
- `hf buckets list` — List buckets or files in a bucket. `[--human-readable --tree --recursive --format CHOICE --quiet]`
- `hf buckets move FROM_ID TO_ID` — Move (rename) a bucket to a new name or namespace.
- `hf buckets remove ARGUMENT` — Remove files from a bucket. `[--recursive --yes --dry-run --include TEXT --exclude TEXT --quiet]`
- `hf buckets sync` — Sync files between local directory and a bucket. `[--delete --ignore-times --ignore-sizes --plan TEXT --apply TEXT --dry-run --include TEXT --exclude TEXT --filter-from TEXT --existing --ignore-existing --verbose --quiet]`
## Installed CLI Compatibility
### `hf cache` — Manage local cache directory.
For newer `circleci` builds that expose domain subcommands (for example `pipeline`, `project`, `trigger`) but not `api`:
- `hf cache list` — List cached repositories or revisions. `[--cache-dir TEXT --revisions --filter TEXT --format CHOICE --quiet --sort CHOICE --limit INTEGER]`
- `hf cache prune` — Remove detached revisions from the cache. `[--cache-dir TEXT --yes --dry-run]`
- `hf cache rm TARGETS` — Remove cached repositories or revisions. `[--cache-dir TEXT --yes --dry-run]`
- `hf cache verify REPO_ID` — Verify checksums for a single repo revision from cache or a local directory. `[--type CHOICE --revision TEXT --cache-dir TEXT --local-dir TEXT --fail-on-missing-files --fail-on-extra-files]`
- Verify available commands first with `circleci help`.
- Use only discovered subcommands from help output.
- Prefer `circleci pipeline list|create|run` and `circleci trigger ...` for pipeline operations.
- For cloud job logs, use supported platform tools (CircleCI app/UI or connected CircleCI MCP tooling) if the CLI does not expose a logs command.
### `hf collections` — Interact with collections on the Hub.
## Output Contract
- `hf collections add-item COLLECTION_SLUG ITEM_ID ITEM_TYPE` — Add an item to a collection. `[--note TEXT --exists-ok]`
- `hf collections create TITLE` — Create a new collection on the Hub. `[--namespace TEXT --description TEXT --private --exists-ok]`
- `hf collections delete COLLECTION_SLUG` — Delete a collection from the Hub. `[--missing-ok]`
- `hf collections delete-item COLLECTION_SLUG ITEM_OBJECT_ID` — Delete an item from a collection. `[--missing-ok]`
- `hf collections info COLLECTION_SLUG` — Get info about a collection on the Hub. Output is in JSON format.
- `hf collections list` — List collections on the Hub. `[--owner TEXT --item TEXT --sort CHOICE --limit INTEGER --format CHOICE --quiet]`
- `hf collections update COLLECTION_SLUG` — Update a collection's metadata on the Hub. `[--title TEXT --description TEXT --position INTEGER --private --theme TEXT]`
- `hf collections update-item COLLECTION_SLUG ITEM_OBJECT_ID` — Update an item in a collection. `[--note TEXT --position INTEGER]`
Provide:
### `hf datasets` — Interact with datasets on the Hub.
1. Commands run and purpose.
2. Key outputs (pipeline/workflow/job ids, status, failing step).
3. Actions taken (rerun/trigger/validate) and why.
4. Remaining blockers and next recommended CLI command.
- `hf datasets info DATASET_ID` — Get info about a dataset on the Hub. Output is in JSON format. `[--revision TEXT --expand TEXT]`
- `hf datasets list` — List datasets on the Hub. `[--search TEXT --author TEXT --filter TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE --quiet]`
- `hf datasets parquet DATASET_ID` — List parquet file URLs available for a dataset. `[--subset TEXT --split TEXT --format CHOICE --quiet]`
- `hf datasets sql SQL` — Execute a raw SQL query with DuckDB against dataset parquet URLs. `[--format CHOICE]`
### `hf discussions` — Manage discussions and pull requests on the Hub.
- `hf discussions close REPO_ID NUM` — Close a discussion or pull request. `[--comment TEXT --yes --type CHOICE]`
- `hf discussions comment REPO_ID NUM` — Comment on a discussion or pull request. `[--body TEXT --body-file PATH --type CHOICE]`
- `hf discussions create REPO_ID --title TEXT` — Create a new discussion or pull request on a repo. `[--body TEXT --body-file PATH --pull-request --type CHOICE]`
- `hf discussions diff REPO_ID NUM` — Show the diff of a pull request. `[--type CHOICE]`
- `hf discussions info REPO_ID NUM` — Get info about a discussion or pull request. `[--comments --diff --no-color --type CHOICE --format CHOICE]`
- `hf discussions list REPO_ID` — List discussions and pull requests on a repo. `[--status CHOICE --kind CHOICE --author TEXT --limit INTEGER --type CHOICE --format CHOICE --quiet]`
- `hf discussions merge REPO_ID NUM` — Merge a pull request. `[--comment TEXT --yes --type CHOICE]`
- `hf discussions rename REPO_ID NUM NEW_TITLE` — Rename a discussion or pull request. `[--type CHOICE]`
- `hf discussions reopen REPO_ID NUM` — Reopen a closed discussion or pull request. `[--comment TEXT --yes --type CHOICE]`
### `hf endpoints` — Manage Hugging Face Inference Endpoints.
- `hf endpoints catalog deploy --repo TEXT` — Deploy an Inference Endpoint from the Model Catalog. `[--name TEXT --accelerator TEXT --namespace TEXT]`
- `hf endpoints catalog list` — List available Catalog models.
- `hf endpoints delete NAME` — Delete an Inference Endpoint permanently. `[--namespace TEXT --yes]`
- `hf endpoints deploy NAME --repo TEXT --framework TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --region TEXT --vendor TEXT` — Deploy an Inference Endpoint from a Hub repository. `[--namespace TEXT --task TEXT --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric CHOICE --scaling-threshold FLOAT]`
- `hf endpoints describe NAME` — Get information about an existing endpoint. `[--namespace TEXT]`
- `hf endpoints list` — Lists all Inference Endpoints for the given namespace. `[--namespace TEXT --format CHOICE --quiet]`
- `hf endpoints pause NAME` — Pause an Inference Endpoint. `[--namespace TEXT]`
- `hf endpoints resume NAME` — Resume an Inference Endpoint. `[--namespace TEXT --fail-if-already-running]`
- `hf endpoints scale-to-zero NAME` — Scale an Inference Endpoint to zero. `[--namespace TEXT]`
- `hf endpoints update NAME` — Update an existing endpoint. `[--namespace TEXT --repo TEXT --accelerator TEXT --instance-size TEXT --instance-type TEXT --framework TEXT --revision TEXT --task TEXT --min-replica INTEGER --max-replica INTEGER --scale-to-zero-timeout INTEGER --scaling-metric CHOICE --scaling-threshold FLOAT]`
### `hf extensions` — Manage hf CLI extensions.
- `hf extensions exec NAME` — Execute an installed extension.
- `hf extensions install REPO_ID` — Install an extension from a public GitHub repository. `[--force]`
- `hf extensions list` — List installed extension commands. `[--format CHOICE --quiet]`
- `hf extensions remove NAME` — Remove an installed extension.
- `hf extensions search` — Search extensions available on GitHub (tagged with 'hf-extension' topic). `[--format CHOICE --quiet]`
### `hf jobs` — Run and manage Jobs on the Hub.
- `hf jobs cancel JOB_ID` — Cancel a Job `[--namespace TEXT]`
- `hf jobs hardware` — List available hardware options for Jobs
- `hf jobs inspect JOB_IDS` — Display detailed information on one or more Jobs `[--namespace TEXT]`
- `hf jobs logs JOB_ID` — Fetch the logs of a Job. `[--follow --tail INTEGER --namespace TEXT]`
- `hf jobs ps` — List Jobs. `[--all --namespace TEXT --filter TEXT --format TEXT --quiet]`
- `hf jobs run IMAGE COMMAND` — Run a Job. `[--env TEXT --secrets TEXT --label TEXT --env-file TEXT --secrets-file TEXT --flavor CHOICE --timeout TEXT --detach --namespace TEXT]`
- `hf jobs scheduled delete SCHEDULED_JOB_ID` — Delete a scheduled Job. `[--namespace TEXT]`
- `hf jobs scheduled inspect SCHEDULED_JOB_IDS` — Display detailed information on one or more scheduled Jobs `[--namespace TEXT]`
- `hf jobs scheduled ps` — List scheduled Jobs `[--all --namespace TEXT --filter TEXT --format TEXT --quiet]`
- `hf jobs scheduled resume SCHEDULED_JOB_ID` — Resume (unpause) a scheduled Job. `[--namespace TEXT]`
- `hf jobs scheduled run SCHEDULE IMAGE COMMAND` — Schedule a Job. `[--suspend --concurrency --env TEXT --secrets TEXT --label TEXT --env-file TEXT --secrets-file TEXT --flavor CHOICE --timeout TEXT --namespace TEXT]`
- `hf jobs scheduled suspend SCHEDULED_JOB_ID` — Suspend (pause) a scheduled Job. `[--namespace TEXT]`
- `hf jobs scheduled uv run SCHEDULE SCRIPT` — Run a UV script (local file or URL) on HF infrastructure `[--suspend --concurrency --image TEXT --flavor CHOICE --env TEXT --secrets TEXT --label TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --namespace TEXT --with TEXT --python TEXT]`
- `hf jobs stats` — Fetch the resource usage statistics and metrics of Jobs `[--namespace TEXT]`
- `hf jobs uv run SCRIPT` — Run a UV script (local file or URL) on HF infrastructure `[--image TEXT --flavor CHOICE --env TEXT --secrets TEXT --label TEXT --env-file TEXT --secrets-file TEXT --timeout TEXT --detach --namespace TEXT --with TEXT --python TEXT]`
### `hf models` — Interact with models on the Hub.
- `hf models info MODEL_ID` — Get info about a model on the Hub. Output is in JSON format. `[--revision TEXT --expand TEXT]`
- `hf models list` — List models on the Hub. `[--search TEXT --author TEXT --filter TEXT --num-parameters TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE --quiet]`
### `hf papers` — Interact with papers on the Hub.
- `hf papers list` — List daily papers on the Hub. `[--date TEXT --sort CHOICE --limit INTEGER --format CHOICE --quiet]`
### `hf repos` — Manage repos on the Hub.
- `hf repos branch create REPO_ID BRANCH` — Create a new branch for a repo on the Hub. `[--revision TEXT --type CHOICE --exist-ok]`
- `hf repos branch delete REPO_ID BRANCH` — Delete a branch from a repo on the Hub. `[--type CHOICE]`
- `hf repos create REPO_ID` — Create a new repo on the Hub. `[--type CHOICE --space-sdk TEXT --private --exist-ok --resource-group-id TEXT]`
- `hf repos delete REPO_ID` — Delete a repo from the Hub. This is an irreversible operation. `[--type CHOICE --missing-ok]`
- `hf repos delete-files REPO_ID PATTERNS` — Delete files from a repo on the Hub. `[--type CHOICE --revision TEXT --commit-message TEXT --commit-description TEXT --create-pr]`
- `hf repos duplicate FROM_ID` — Duplicate a repo on the Hub (model, dataset, or Space). `[--type CHOICE --private --exist-ok]`
- `hf repos move FROM_ID TO_ID` — Move a repository from a namespace to another namespace. `[--type CHOICE]`
- `hf repos settings REPO_ID` — Update the settings of a repository. `[--gated CHOICE --private --type CHOICE]`
- `hf repos tag create REPO_ID TAG` — Create a tag for a repo. `[--message TEXT --revision TEXT --type CHOICE]`
- `hf repos tag delete REPO_ID TAG` — Delete a tag for a repo. `[--yes --type CHOICE]`
- `hf repos tag list REPO_ID` — List tags for a repo. `[--type CHOICE]`
### `hf skills` — Manage skills for AI assistants.
- `hf skills add` — Download a skill and install it for an AI assistant. `[--claude --codex --cursor --opencode --global --dest PATH --force]`
- `hf skills preview` — Print the generated SKILL.md to stdout.
### `hf spaces` — Interact with spaces on the Hub.
- `hf spaces dev-mode SPACE_ID` — Enable or disable dev mode on a Space. `[--stop]`
- `hf spaces hot-reload SPACE_ID` — Hot-reload any Python file of a Space without a full rebuild + restart. `[--local-file TEXT --skip-checks --skip-summary]`
- `hf spaces info SPACE_ID` — Get info about a space on the Hub. Output is in JSON format. `[--revision TEXT --expand TEXT]`
- `hf spaces list` — List spaces on the Hub. `[--search TEXT --author TEXT --filter TEXT --sort CHOICE --limit INTEGER --expand TEXT --format CHOICE --quiet]`
### `hf webhooks` — Manage webhooks on the Hub.
- `hf webhooks create --watch TEXT` — Create a new webhook. `[--url TEXT --job-id TEXT --domain CHOICE --secret TEXT]`
- `hf webhooks delete WEBHOOK_ID` — Delete a webhook permanently. `[--yes]`
- `hf webhooks disable WEBHOOK_ID` — Disable an active webhook.
- `hf webhooks enable WEBHOOK_ID` — Enable a disabled webhook.
- `hf webhooks info WEBHOOK_ID` — Show full details for a single webhook as JSON.
- `hf webhooks list` — List all webhooks for the current user. `[--format CHOICE --quiet]`
- `hf webhooks update WEBHOOK_ID` — Update an existing webhook. Only provided options are changed. `[--url TEXT --watch TEXT --domain CHOICE --secret TEXT]`
## Common options
- `--format` — Output format: `--format json` (or `--json`) or `--format table` (default).
- `-q / --quiet` — Minimal output.
- `--revision` — Git revision id which can be a branch name, a tag, or a commit hash.
- `--token` — Use a User Access Token. Prefer setting `HF_TOKEN` env var instead of passing `--token`.
- `--type` — The type of repository (model, dataset, or space).
## Tips
- Use `hf <command> --help` for full options, descriptions, usage, and real-world examples
- Authenticate with `HF_TOKEN` env var (recommended) or with `--token`
+2 -2
View File
@@ -1,3 +1,3 @@
interface:
display_name: "CircleCI CLI"
short_description: "Operate and troubleshoot CircleCI with the CLI"
display_name: "HF CLI"
short_description: "Manage Hub repos, models, datasets, and Spaces with the Hugging Face CLI"
+165 -25
View File
@@ -1,35 +1,175 @@
---
name: setup
description: Verify Daloopa MCP connection and show available skills
name: mixpanel-headless-setup
description: This skill installs mixpanel_headless, pandas, numpy, matplotlib, seaborn, networkx, anytree, scipy (and pyarrow on Python 3.11+), then verifies Mixpanel credentials. It should be invoked when setting up a new environment for Mixpanel data analysis, when dependencies are missing, or when configuring service account or OAuth credentials for the first time.
disable-model-invocation: false
allowed-tools: Bash
---
Walk the user through verifying their Daloopa setup for Codex or ChatGPT. Be conversational and helpful.
# mixpanel-headless — Setup
## Step 1: Verify Runtime
Confirm the user is working in Codex, ChatGPT, or another OpenAI environment that can use skills. Explain that this skill checks whether the Daloopa MCP tools are available.
Install dependencies and verify credentials for CodeMode analytics.
## Step 2: Verify MCP Connection
This plugin connects to two Daloopa MCP servers:
- **daloopa** (`mcp.daloopa.com/server/mcp`) - Financial data (fundamentals, KPIs, SEC filings)
- **daloopa-docs** (`docs.daloopa.com/mcp`) - Daloopa knowledgebase (API docs, how-tos, usage help)
## Run Setup
Run a quick test by calling `discover_companies` with a well-known ticker like "AAPL" to confirm the data MCP server is connected and responding. Show the user the result.
Before running bundled scripts, set `SKILL_DIR` to the absolute path of this
`skills/setup` directory.
If this fails:
- Check that `.mcp.json` is present and configured for the Daloopa MCP servers.
- In Codex, reinstall or reload the plugin after changing MCP configuration.
- In ChatGPT, verify that the Daloopa MCP connector or equivalent tool access is enabled.
- If the server returns `401` or `Reauthentication required`, restart the Daloopa OAuth/login flow in the current environment.
- On first use, OAuth may open a browser window for Daloopa login.
```bash
bash $SKILL_DIR/scripts/setup.sh
```
## Step 3: Quick Tour
Tell the user about the available analysis skills. Use natural-language examples such as:
- "Create a tearsheet for AAPL."
- "Review MSFT earnings and guidance."
- "Build a DCF valuation for NVDA."
- "Create an industry comp sheet for AAPL and peers."
This will:
1. Verify Python 3.10+ is available
2. Install `mixpanel_headless`, `pandas`, `numpy`, `matplotlib`, `seaborn`, `networkx>=3.0`, `anytree>=2.8.0`, `scipy`, and `pyarrow>=17.0` on Python 3.11+ (tries uv, pip in order)
3. Verify all packages import successfully (including pyarrow on 3.11+, networkx, anytree, and scipy)
4. Check for configured Mixpanel credentials (single schema — Account → Project → Workspace)
Each reporting skill saves generated files to the `reports/` directory when file access is available.
## Check Credentials
## Step 4: Note on Enhanced Features
For file-heavy workflows such as Word research notes, Excel models, and pitch decks, use the local document, spreadsheet, or presentation generation workflow available in the current OpenAI environment. If a file cannot be generated in the current environment, provide the complete structured content and explain the limitation.
After installation, check the active session:
```bash
python3 $SKILL_DIR/../mixpanelyst/scripts/auth_manager.py session
```
Parse the JSON `state` field:
- **`ok`** — credentials configured. Show `account.name` → project `project.id` and proceed to verification.
- **`needs_account`** — no account configured. Read `next` for onboarding suggestions and follow "If Credentials Are Missing" below.
- **`needs_project`** — account configured but no project pinned. Suggest `mp project list` then `mp project use <id>`.
- **`error`** — show `error.message`. If `error.actionable` is true, the message names a concrete next command.
## If Credentials Are Missing
If no credentials are configured, guide the user to one of these methods:
### Recommended: `mp login`
The frictionless one-shot path. Tell the user to run:
```bash
mp login
```
`mp login` runs the right auth flow for the environment, derives the
account name from `/me`, and pins a default project. For laptops with a
usable browser, this opens the PKCE flow; for environments with
`MP_USERNAME` + `MP_SECRET` set, it skips the browser and uses the
service-account path; for `MP_OAUTH_TOKEN` set, it uses the static
bearer.
Region behavior:
- `service_account` and `oauth_token` paths probe `us → eu → in` when
`--region` is omitted.
- `oauth_browser` (the bare-`mp login` default) defaults to `us`. EU and
India browser users must pass `--region eu` or `--region in`.
Useful flags: `--name NAME`, `--region us|eu|in`, `--project ID`,
`--service-account`, `--token-env VAR`, `--no-browser`, `--secret-stdin`.
### Alternative: Guided Setup (explicit account add)
Use the `mixpanel-auth` skill's account-add workflow for a
step-by-step walkthrough. The workflow never prompts for secrets in
conversation — it instructs the user to run `mp account add ...`
themselves so the secret is read with hidden input. Use this path when
the user wants explicit control over the account name, region, and type
at registration time.
### Alternative: Service-Account Environment Variables (temporary)
For quick testing, set all four variables in the shell — the resolver
picks them up directly without account registration:
```bash
export MP_USERNAME="service-account-username"
export MP_SECRET="service-account-secret"
export MP_PROJECT_ID="12345"
export MP_REGION="us" # or "eu", "in"
```
### Alternative: Raw OAuth Bearer Token (best for agents / CI)
If the user has an OAuth 2.0 access token from another source, they can use
it directly without the PKCE browser flow:
```bash
export MP_OAUTH_TOKEN="<bearer-token>"
export MP_PROJECT_ID="12345"
export MP_REGION="us" # or "eu", "in"
```
This is the recommended mode for non-interactive contexts. The full
service-account env-var set (`MP_USERNAME` + `MP_SECRET` + `MP_PROJECT_ID`
+ `MP_REGION`) takes precedence when both sets are complete.
## Remote Environment
If running inside a remote or sandboxed agent environment, credentials work differently:
- **OAuth login and interactive account setup are NOT available** (no browser, no host terminal access)
- Credentials must be configured on the **host machine** before starting the remote session
### If No Credentials Found in the Remote Session
Tell the user:
> No Mixpanel credentials found in this remote session.
>
> On your **host machine** (outside the remote session), run:
> ```
> mp account export-bridge --to ~/.claude/mixpanel/auth.json
> ```
> This writes a v2 bridge file embedding your account record (and any
> oauth_browser tokens) so the remote session can read your credentials
> at startup.
>
> Then **start a new remote session** — credentials will be available automatically.
Do NOT suggest the account-login or account-add interactive workflows — these won't work inside remote sessions without browser or terminal access.
### If Bridge File Found But Token Expired
The library will auto-refresh the OAuth token via the on-disk refresh
token (no browser needed). If refresh fails:
> Your OAuth session has expired and could not be refreshed.
> On your host machine, run:
> ```
> mp login --name personal # re-authenticate (or `mp account login personal`)
> mp account export-bridge --to ~/.claude/mixpanel/auth.json
> ```
> Then start a new remote session.
## Verify Everything Works
```bash
python3 $SKILL_DIR/../mixpanelyst/scripts/auth_manager.py account test
```
The subcommand never raises — read `result.ok` to determine outcome.
- `result.ok: true` → setup is complete; the user can ask analytics questions.
- `result.ok: false` → suggest the `mixpanel-auth` account-test workflow for detailed diagnostics.
## Post-Setup: Explore Your Data
Once authenticated, these slash commands help orient the user:
- `mixpanel-auth` project-list workflow — discover all accessible projects via `/me`
- `mixpanel-auth` session workflow — see active account / project / workspace
- `mixpanel-auth` project-use workflow — switch to a different project
- `mixpanel-auth` target-add workflow — save a named cursor position
The user can also construct a Workspace targeting a specific account / project /
workspace directly:
```python
import mixpanel_headless as mp
ws = mp.Workspace() # default session
ws = mp.Workspace(account="team") # named account
ws = mp.Workspace(project="67890") # explicit project (active account)
ws = mp.Workspace(account="team", project="67890") # both axes
ws.use(project="98765").events() # in-session switch (no re-auth)
```
_The mixpanelyst skill auto-triggers on analytics questions. For the analytical frameworks that guide investigations, see [analytical-frameworks.md](../mixpanelyst/references/analytical-frameworks.md). For the complete Python API, see [python-api.md](../mixpanelyst/references/python-api.md)._
-6
View File
@@ -1,6 +0,0 @@
interface:
display_name: Setup
short_description: Verify Daloopa MCP connection and show available skills
default_prompt: Verify my Daloopa MCP connection.
policy:
allow_implicit_invocation: true
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# Install mixpanel_headless and pandas for CodeMode analytics
set -euo pipefail
echo "=== mixpanel-headless — CodeMode Setup ==="
echo ""
# Find Python 3.10+
python_cmd=""
for cmd in python3 python; do
if command -v "$cmd" &>/dev/null; then
major=$("$cmd" -c "import sys; print(sys.version_info.major)" 2>/dev/null || echo 0)
minor=$("$cmd" -c "import sys; print(sys.version_info.minor)" 2>/dev/null || echo 0)
if [ "$major" -gt 3 ] || ([ "$major" -eq 3 ] && [ "$minor" -ge 10 ]); then
version=$("$cmd" -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}')")
python_cmd="$cmd"
echo "✓ Python $version ($cmd)"
break
fi
fi
done
if [ -z "$python_cmd" ]; then
echo "✗ Python 3.10+ required but not found."
echo " Install from https://python.org or via your package manager."
exit 1
fi
# Install packages
MIXPANEL_HEADLESS_PKG="mixpanel-headless"
DEPS=(pandas numpy matplotlib seaborn 'networkx>=3.0' 'anytree>=2.8.0' scipy)
# pyarrow is only needed on Python 3.11+ (for pandas 3.x Arrow-backed dtypes)
if [ "$minor" -ge 11 ]; then
DEPS+=('pyarrow>=17.0')
fi
echo ""
echo "Installing mixpanel-headless (import name: mixpanel_headless) and dependencies..."
if command -v uv &>/dev/null; then
echo " (using uv)"
uv pip install --python "$python_cmd" "$MIXPANEL_HEADLESS_PKG" "${DEPS[@]}" || { echo " ⚠ Virtualenv install failed, trying system install..."; uv pip install --system --python "$python_cmd" "$MIXPANEL_HEADLESS_PKG" "${DEPS[@]}"; }
elif "$python_cmd" -m pip --version &>/dev/null; then
echo " (using pip via $python_cmd)"
"$python_cmd" -m pip install "$MIXPANEL_HEADLESS_PKG" "${DEPS[@]}"
else
echo "✗ No package manager found. Install pip or uv."
echo " Recommended: https://docs.astral.sh/uv/"
exit 1
fi
# Verify imports
echo ""
echo "Verifying installation..."
"$python_cmd" -c "
import sys
import mixpanel_headless as mp
import pandas as pd
import numpy as np
import matplotlib
import seaborn as sns
import networkx as nx
import anytree
import scipy
print(f'✓ mixpanel_headless installed')
print(f'✓ pandas {pd.__version__}')
if sys.version_info >= (3, 11):
import pyarrow as pa
print(f'✓ pyarrow {pa.__version__}')
print(f'✓ numpy {np.__version__}')
print(f'✓ matplotlib {matplotlib.__version__}')
print(f'✓ seaborn {sns.__version__}')
print(f'✓ networkx {nx.__version__}')
print(f'✓ anytree {anytree.__version__}')
print(f'✓ scipy {scipy.__version__}')
" || { echo "✗ Import verification failed"; exit 1; }
# Check credentials
echo ""
echo "Checking Mixpanel credentials..."
"$python_cmd" -c "
import os, sys
# 1) Service-account env quad
sa_quad = ['MP_USERNAME', 'MP_SECRET', 'MP_PROJECT_ID', 'MP_REGION']
sa_set = [v for v in sa_quad if os.environ.get(v)]
if len(sa_set) == len(sa_quad):
print('✓ Service-account env quad is fully set (MP_USERNAME + MP_SECRET + MP_PROJECT_ID + MP_REGION)')
sys.exit(0)
elif sa_set:
missing = [v for v in sa_quad if not os.environ.get(v)]
print(f'⚠ Partial service-account env config — missing: {\", \".join(missing)}')
# 2) OAuth-token env triple
oauth_triple = ['MP_OAUTH_TOKEN', 'MP_PROJECT_ID', 'MP_REGION']
oauth_set = [v for v in oauth_triple if os.environ.get(v)]
if len(oauth_set) == len(oauth_triple):
print('✓ OAuth-token env triple is fully set (MP_OAUTH_TOKEN + MP_PROJECT_ID + MP_REGION)')
sys.exit(0)
# 3) Persisted accounts in ~/.mp/config.toml
try:
import mixpanel_headless as mp
accounts = mp.accounts.list()
if accounts:
active = next((a for a in accounts if a.is_active), None)
names = ', '.join(a.name for a in accounts)
print(f'✓ {len(accounts)} account(s) in ~/.mp/config.toml: {names}')
if active:
print(f' Active: {active.name} ({active.type}, {active.region})')
else:
print('⚠ No active account selected. Run: mp account use <name>')
else:
print('⚠ No accounts configured yet.')
print(' Recommended: mp login # one-shot frictionless login')
print(' Service account: mp account add team --type service_account --username sa_xxx --project 12345 --region us')
except Exception as e:
print(f'⚠ Could not read ~/.mp/config.toml: {e}')
print(' Run `mp login`, set env vars (service-account quad or OAuth triple), or run mp account add ...')
"
# Remote-session detection: check for bridge file
if [ -d "/sessions" ] || [ -n "${CODEX_CLOUD:-}" ]; then
echo ""
echo "Remote environment detected."
BRIDGE_FOUND=""
for f in "$HOME/.claude/mixpanel/auth.json"; do
if [ -f "$f" ]; then
echo "✓ Auth bridge file found: $f"
"$python_cmd" -c "
import json, sys
try:
with open(sys.argv[1]) as fh:
bridge = json.load(fh)
if bridge.get('version') != 2:
print(f' ⚠ Unexpected bridge version: {bridge.get(\"version\")} (expected 2)')
account = bridge.get('account', {})
print(f' Account: {account.get(\"name\", \"?\")} ({account.get(\"type\", \"?\")}, {account.get(\"region\", \"?\")})')
project = bridge.get('project') or account.get('default_project')
if project:
print(f' Project: {project}')
if bridge.get('workspace'):
print(f' Workspace: {bridge[\"workspace\"]}')
headers = bridge.get('headers') or {}
if headers:
print(f' Custom headers: {len(headers)} entr{\"y\" if len(headers) == 1 else \"ies\"} ✓')
tokens = bridge.get('tokens')
if tokens and tokens.get('expires_at'):
print(f' Token expires: {tokens[\"expires_at\"]}')
except Exception as e:
print(f' Error reading bridge file: {e}')
" "$f"
BRIDGE_FOUND=1
break
fi
done
if [ -z "$BRIDGE_FOUND" ]; then
echo "⚠ No auth bridge file found."
echo " On your HOST machine, run:"
echo " mp account export-bridge --to ~/.claude/mixpanel/auth.json"
echo " Then start a new remote session."
fi
fi
echo ""
echo "=== Setup complete ==="