@clawhub-jolestar-21112cba96
Operate Matrix Client-Server API through UXC with a curated OpenAPI schema, bearer-token auth, and homeserver-aware messaging guardrails.
---
name: matrix-openapi-skill
description: Operate Matrix Client-Server API through UXC with a curated OpenAPI schema, bearer-token auth, and homeserver-aware messaging guardrails.
---
# Matrix Client-Server API Skill
Use this skill to run Matrix Client-Server operations through `uxc` + OpenAPI.
Reuse the `uxc` skill for shared execution, auth, and error-handling guidance.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to your Matrix homeserver's client-server base URL, usually `https://<homeserver>/_matrix/client/v3`.
- Access to the curated OpenAPI schema URL:
- `https://raw.githubusercontent.com/holon-run/uxc/main/skills/matrix-openapi-skill/references/matrix-client-server.openapi.json`
- A Matrix access token for the target homeserver.
## Scope
This skill covers a practical request/response Matrix surface:
- token owner lookup
- joined room discovery
- room state lookup
- `/sync` polling reads, including daemon-backed poll subscribe
- user profile and presence lookup
- room message sends
This skill does **not** cover:
- login, SSO, device registration, or generic token acquisition flows
- appservice, federation, or bot framework abstractions
- webhook or long-running event receiver runtime
- full Matrix spec coverage
## Homeserver Base URL
Matrix is homeserver-specific. The endpoint you link must include the Matrix client-server base path:
- typical form: `https://<homeserver>/_matrix/client/v3`
- example form: `https://matrix.org/_matrix/client/v3`
Do not link only the homeserver origin without `/_matrix/client/v3`.
## Authentication
Matrix Client-Server API uses `Authorization: Bearer <access_token>`.
Preferred path for OAuth-aware homeservers:
```bash
uxc auth oauth start matrix-oauth \
--endpoint https://matrix.org/_matrix/client/v3 \
--redirect-uri http://127.0.0.1:8788/callback \
--client-id <client_id>
uxc auth oauth complete matrix-oauth \
--session-id <session_id> \
--authorization-response 'http://127.0.0.1:8788/callback?code=...'
uxc auth binding add \
--id matrix-oauth \
--host matrix.org \
--path-prefix /_matrix/client/v3 \
--scheme https \
--credential matrix-oauth \
--priority 100
```
Fallback path when you already have an access token:
```bash
uxc auth credential set matrix-access \
--auth-type bearer \
--secret-env MATRIX_ACCESS_TOKEN
uxc auth binding add \
--id matrix-access \
--host matrix.org \
--path-prefix /_matrix/client/v3 \
--scheme https \
--credential matrix-access \
--priority 100
```
If your homeserver is not `matrix.org`, replace the host while keeping the same path prefix. Validate the active mapping when auth looks wrong:
```bash
uxc auth binding match https://matrix.org/_matrix/client/v3
```
Notes:
- `uxc auth oauth` works only for homeservers that expose Matrix OAuth metadata.
- Prefer a loopback redirect URI on an uncommon high port, such as `http://127.0.0.1:8788/callback`, to avoid conflicts with local services on common ports.
- Legacy Matrix login and SSO fallback flows are not covered by this skill yet.
## Core Workflow
1. Use the fixed link command by default:
- `command -v matrix-openapi-cli`
- If missing, create it:
`uxc link matrix-openapi-cli https://matrix.org/_matrix/client/v3 --schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/matrix-openapi-skill/references/matrix-client-server.openapi.json`
- `matrix-openapi-cli -h`
2. Inspect operation schema first:
- `matrix-openapi-cli get:/account/whoami -h`
- `matrix-openapi-cli get:/sync -h`
- `matrix-openapi-cli put:/rooms/{roomId}/send/{eventType}/{txnId} -h`
3. Prefer read validation before writes:
- `matrix-openapi-cli get:/account/whoami`
- `matrix-openapi-cli get:/joined_rooms`
- `matrix-openapi-cli get:/rooms/{roomId}/state roomId=!abc123:example.org`
4. Execute with key/value or positional JSON:
- key/value:
`matrix-openapi-cli get:/sync timeout=30000 filter={"room":{"timeline":{"limit":10}}}`
- positional JSON:
`matrix-openapi-cli put:/rooms/{roomId}/send/{eventType}/{txnId} '{"roomId":"!abc123:example.org","eventType":"m.room.message","txnId":"uxc-001","msgtype":"m.text","body":"Hello from UXC"}'`
5. For background `/sync` polling, call `uxc subscribe start` directly against the homeserver base URL:
- `uxc subscribe start https://matrix.org/_matrix/client/v3 get:/sync --auth matrix-oauth --mode poll --poll-config '{"interval_secs":2,"extract_items_pointer":"/rooms/join/!abc123:example.org/timeline/events","missing_extract_items_pointer_as_empty":true,"request_cursor_arg":"since","response_cursor_pointer":"/next_batch","checkpoint_strategy":{"type":"cursor_only"}}' --sink file:$HOME/.uxc/subscriptions/matrix-sync.ndjson timeout=1000 'filter={"room":{"rooms":["!abc123:example.org"],"timeline":{"limit":5}}}'`
## Operation Groups
### Session / Discovery
- `get:/account/whoami`
- `get:/joined_rooms`
- `get:/sync`
### Room Reads
- `get:/rooms/{roomId}/state`
- `get:/rooms/{roomId}/state/{eventType}/{stateKey}`
### User Reads
- `get:/profile/{userId}`
- `get:/presence/{userId}/status`
### Messaging
- `put:/rooms/{roomId}/send/{eventType}/{txnId}`
## Guardrails
- Keep automation on the JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- `get:/sync` works both as a normal polling/read call and as a validated daemon-backed poll subscription when invoked through `uxc subscribe start`.
- For room-scoped `/sync` subscriptions, set `missing_extract_items_pointer_as_empty=true` so sparse responses without new room timeline events are treated as an empty batch instead of a fatal error.
- `put:/rooms/{roomId}/send/{eventType}/{txnId}` is high-risk and should default to `m.room.message` text sends unless the user explicitly asks for another event type.
- Reuse a unique `txnId` per send attempt to avoid accidental duplicates.
- Many homeservers restrict presence visibility and room state/event access based on membership and server policy; auth success does not imply every room or profile read will succeed.
- `matrix-openapi-cli <operation> ...` is equivalent to `uxc <homeserver_client_base> --schema-url <matrix_openapi_schema> <operation> ...`.
## References
- Usage patterns: `references/usage-patterns.md`
- Curated OpenAPI schema: `references/matrix-client-server.openapi.json`
- Matrix Client-Server API: https://spec.matrix.org/latest/client-server-api/
- Matrix spec source: https://github.com/matrix-org/matrix-spec/tree/main/data/api/client-server
FILE:agents/openai.yaml
interface:
display_name: "Matrix Client-Server API"
short_description: "Operate Matrix Client-Server API via UXC + curated OpenAPI schema"
default_prompt: "Use $matrix-openapi-skill to discover and execute Matrix Client-Server API operations through UXC with bearer-token auth, homeserver-aware setup, and read-first messaging guardrails."
FILE:references/matrix-client-server.openapi.json
{
"openapi": "3.0.3",
"info": {
"title": "Matrix Client-Server API (Curated)",
"version": "1.0.0",
"description": "Curated Matrix client-server OpenAPI schema for common read/write workflows through UXC."
},
"servers": [
{
"url": "https://matrix.org/_matrix/client/v3",
"description": "Example Matrix homeserver client-server base URL. Replace with your own homeserver base when linking UXC."
}
],
"security": [
{
"bearerAuth": []
}
],
"paths": {
"/account/whoami": {
"get": {
"operationId": "get:/account/whoami",
"summary": "Get information about the owner of an access token.",
"description": "Returns the user ID and optional device metadata for the supplied access token.",
"tags": [
"session"
],
"responses": {
"200": {
"description": "The token belongs to a known user.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WhoAmIResponse"
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/joined_rooms": {
"get": {
"operationId": "get:/joined_rooms",
"summary": "List the rooms the user is currently joined to.",
"description": "Returns the room IDs for the current user membership state of join.",
"tags": [
"rooms"
],
"responses": {
"200": {
"description": "A list of room IDs.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/JoinedRoomsResponse"
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/rooms/{roomId}/state": {
"get": {
"operationId": "get:/rooms/{roomId}/state",
"summary": "Get all state events in the current state of a room.",
"description": "Returns the room's current state events visible to the access token.",
"tags": [
"rooms"
],
"parameters": [
{
"name": "roomId",
"in": "path",
"required": true,
"description": "The Matrix room ID, for example `!room:example.org`.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The current state events for the room.",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixStateEvent"
}
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The caller cannot view the room state.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/rooms/{roomId}/state/{eventType}/{stateKey}": {
"get": {
"operationId": "get:/rooms/{roomId}/state/{eventType}/{stateKey}",
"summary": "Get a specific state event by type and state key.",
"description": "Returns the content object for a specific state event in the room.",
"tags": [
"rooms"
],
"parameters": [
{
"name": "roomId",
"in": "path",
"required": true,
"description": "The Matrix room ID, for example `!room:example.org`.",
"schema": {
"type": "string"
}
},
{
"name": "eventType",
"in": "path",
"required": true,
"description": "The Matrix event type, for example `m.room.topic`.",
"schema": {
"type": "string"
}
},
{
"name": "stateKey",
"in": "path",
"required": true,
"description": "The Matrix state key. Use the empty string when the state event type requires an empty state key.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The event content object.",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The caller cannot view the requested state event.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "The room or event state was not found.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/sync": {
"get": {
"operationId": "get:/sync",
"summary": "Synchronise the client's state and receive updates.",
"description": "Performs a request/response sync against the homeserver. This is normal invocation only and not a background subscription runtime.",
"tags": [
"sync"
],
"parameters": [
{
"name": "filter",
"in": "query",
"description": "A filter ID or a full JSON filter encoded as a string.",
"schema": {
"type": "string"
}
},
{
"name": "since",
"in": "query",
"description": "A sync token from a previous `/sync` response.",
"schema": {
"type": "string"
}
},
{
"name": "full_state",
"in": "query",
"description": "When true, return the full state for joined rooms.",
"schema": {
"type": "boolean"
}
},
{
"name": "set_presence",
"in": "query",
"description": "Set the client's presence for this sync request.",
"schema": {
"type": "string",
"enum": [
"offline",
"online",
"unavailable"
]
}
},
{
"name": "timeout",
"in": "query",
"description": "Long-poll timeout in milliseconds.",
"schema": {
"type": "integer",
"minimum": 0
}
}
],
"responses": {
"200": {
"description": "A sync snapshot plus a token for the next request.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SyncResponse"
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"429": {
"description": "This request was rate-limited.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RateLimitedError"
}
}
}
}
}
}
},
"/profile/{userId}": {
"get": {
"operationId": "get:/profile/{userId}",
"summary": "Get all profile information for a user.",
"description": "Returns common profile fields such as display name and avatar URL.",
"tags": [
"profile"
],
"parameters": [
{
"name": "userId",
"in": "path",
"required": true,
"description": "The Matrix user ID, for example `@alice:example.org`.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The user's profile information.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserProfile"
}
}
}
},
"404": {
"description": "The user profile was not found or is unavailable.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/presence/{userId}/status": {
"get": {
"operationId": "get:/presence/{userId}/status",
"summary": "Get the presence state for a user.",
"description": "Returns presence, optional status message, and activity hints for the given user.",
"tags": [
"presence"
],
"parameters": [
{
"name": "userId",
"in": "path",
"required": true,
"description": "The Matrix user ID, for example `@alice:example.org`.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "The user's presence state.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PresenceStatus"
}
}
}
},
"401": {
"description": "The access token is invalid or expired.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The caller cannot view the presence state.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/rooms/{roomId}/send/{eventType}/{txnId}": {
"put": {
"operationId": "put:/rooms/{roomId}/send/{eventType}/{txnId}",
"summary": "Send a message event to a room.",
"description": "Sends an event content object to the room using a client-generated transaction ID for idempotency.",
"tags": [
"rooms"
],
"parameters": [
{
"name": "roomId",
"in": "path",
"required": true,
"description": "The Matrix room ID, for example `!room:example.org`.",
"schema": {
"type": "string"
}
},
{
"name": "eventType",
"in": "path",
"required": true,
"description": "The Matrix event type, typically `m.room.message` for chat messages.",
"schema": {
"type": "string"
}
},
{
"name": "txnId",
"in": "path",
"required": true,
"description": "A client-generated transaction ID used for idempotency.",
"schema": {
"type": "string"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SendEventRequest"
},
"example": {
"msgtype": "m.text",
"body": "Hello from UXC"
}
}
}
},
"responses": {
"200": {
"description": "The event was accepted and assigned an event ID.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SendEventResponse"
}
}
}
},
"400": {
"description": "The request body or parameters are invalid.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"403": {
"description": "The caller cannot send this event into the room.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"description": "Matrix access token sent as `Authorization: Bearer <token>`."
}
},
"schemas": {
"WhoAmIResponse": {
"type": "object",
"required": [
"user_id"
],
"properties": {
"user_id": {
"type": "string",
"description": "The Matrix user ID that owns the access token."
},
"device_id": {
"type": "string",
"description": "The device ID associated with the access token, when present."
},
"is_guest": {
"type": "boolean",
"description": "Whether the token belongs to a guest user."
}
},
"additionalProperties": false
},
"JoinedRoomsResponse": {
"type": "object",
"required": [
"joined_rooms"
],
"properties": {
"joined_rooms": {
"type": "array",
"items": {
"type": "string"
},
"description": "Room IDs where the user currently has join membership."
}
},
"additionalProperties": false
},
"MatrixStateEvent": {
"type": "object",
"required": [
"type",
"content"
],
"properties": {
"type": {
"type": "string"
},
"state_key": {
"type": "string"
},
"event_id": {
"type": "string"
},
"room_id": {
"type": "string"
},
"sender": {
"type": "string"
},
"origin_server_ts": {
"type": "integer",
"format": "int64"
},
"unsigned": {
"type": "object",
"additionalProperties": true
},
"content": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true
},
"TimelineChunk": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
},
"limited": {
"type": "boolean"
},
"prev_batch": {
"type": "string"
}
},
"additionalProperties": true
},
"RoomSyncSection": {
"type": "object",
"properties": {
"state": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixStateEvent"
}
}
},
"additionalProperties": true
},
"timeline": {
"$ref": "#/components/schemas/TimelineChunk"
},
"ephemeral": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
}
},
"additionalProperties": true
},
"account_data": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
}
},
"additionalProperties": true
},
"unread_notifications": {
"type": "object",
"properties": {
"notification_count": {
"type": "integer"
},
"highlight_count": {
"type": "integer"
}
},
"additionalProperties": true
}
},
"additionalProperties": true
},
"SyncResponse": {
"type": "object",
"required": [
"next_batch"
],
"properties": {
"next_batch": {
"type": "string",
"description": "Opaque token to supply as `since` on the next `/sync` call."
},
"account_data": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
}
},
"additionalProperties": true
},
"presence": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
}
},
"additionalProperties": true
},
"to_device": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"$ref": "#/components/schemas/MatrixTimelineEvent"
}
}
},
"additionalProperties": true
},
"rooms": {
"type": "object",
"properties": {
"join": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/RoomSyncSection"
}
},
"invite": {
"type": "object",
"additionalProperties": true
},
"leave": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/RoomSyncSection"
}
},
"knock": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true
}
},
"additionalProperties": true
},
"UserProfile": {
"type": "object",
"properties": {
"avatar_url": {
"type": "string",
"description": "The Matrix Content URI (`mxc://...`) for the user's avatar, when present."
},
"displayname": {
"type": "string",
"description": "The user's display name, when present."
}
},
"additionalProperties": true
},
"PresenceStatus": {
"type": "object",
"required": [
"presence"
],
"properties": {
"presence": {
"type": "string",
"enum": [
"online",
"offline",
"unavailable"
]
},
"status_msg": {
"type": "string"
},
"last_active_ago": {
"type": "integer",
"format": "int64"
},
"currently_active": {
"type": "boolean"
}
},
"additionalProperties": true
},
"SendEventRequest": {
"type": "object",
"description": "The event content object. For chat messages, use Matrix message content such as `{ \"msgtype\": \"m.text\", \"body\": \"Hello\" }`.",
"additionalProperties": true,
"properties": {
"msgtype": {
"type": "string"
},
"body": {
"type": "string"
},
"m.relates_to": {
"type": "object",
"additionalProperties": true
}
}
},
"SendEventResponse": {
"type": "object",
"required": [
"event_id"
],
"properties": {
"event_id": {
"type": "string",
"description": "The event ID assigned by the homeserver."
}
},
"additionalProperties": false
},
"MatrixTimelineEvent": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"event_id": {
"type": "string"
},
"room_id": {
"type": "string"
},
"sender": {
"type": "string"
},
"origin_server_ts": {
"type": "integer",
"format": "int64"
},
"state_key": {
"type": "string"
},
"unsigned": {
"type": "object",
"additionalProperties": true
},
"content": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true
},
"ErrorResponse": {
"type": "object",
"required": [
"errcode",
"error"
],
"properties": {
"errcode": {
"type": "string",
"description": "Matrix error code such as `M_UNKNOWN_TOKEN`, `M_FORBIDDEN`, or `M_NOT_FOUND`."
},
"error": {
"type": "string",
"description": "Human-readable error message."
}
},
"additionalProperties": true
},
"RateLimitedError": {
"allOf": [
{
"$ref": "#/components/schemas/ErrorResponse"
},
{
"type": "object",
"properties": {
"retry_after_ms": {
"type": "integer",
"format": "int64",
"description": "Milliseconds to wait before retrying."
}
},
"additionalProperties": true
}
]
}
}
}
}
FILE:references/usage-patterns.md
# Matrix Client-Server API Skill - Usage Patterns
## Link Setup
```bash
command -v matrix-openapi-cli
uxc link matrix-openapi-cli https://matrix.org/_matrix/client/v3 \
--schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/matrix-openapi-skill/references/matrix-client-server.openapi.json
matrix-openapi-cli -h
```
Replace `matrix.org` with your own homeserver when needed.
## Auth Setup
OAuth-aware homeserver:
```bash
uxc auth oauth start matrix-oauth \
--endpoint https://matrix.org/_matrix/client/v3 \
--redirect-uri http://127.0.0.1:8788/callback \
--client-id <client_id>
uxc auth oauth complete matrix-oauth \
--session-id <session_id> \
--authorization-response 'http://127.0.0.1:8788/callback?code=...'
uxc auth binding add \
--id matrix-oauth \
--host matrix.org \
--path-prefix /_matrix/client/v3 \
--scheme https \
--credential matrix-oauth \
--priority 100
```
Use a loopback callback on an uncommon high port to avoid collisions with local services on common ports.
Existing access token:
```bash
uxc auth credential set matrix-access \
--auth-type bearer \
--secret-env MATRIX_ACCESS_TOKEN
uxc auth binding add \
--id matrix-access \
--host matrix.org \
--path-prefix /_matrix/client/v3 \
--scheme https \
--credential matrix-access \
--priority 100
```
## Read Examples
```bash
# Confirm token owner
matrix-openapi-cli get:/account/whoami
# List joined rooms
matrix-openapi-cli get:/joined_rooms
# Read all current state events for a room
matrix-openapi-cli get:/rooms/{roomId}/state roomId=!abc123:example.org
# Read one named state event
matrix-openapi-cli get:/rooms/{roomId}/state/{eventType}/{stateKey} \
roomId=!abc123:example.org \
eventType=m.room.topic \
stateKey=
# Poll /sync once
matrix-openapi-cli get:/sync timeout=30000
# Run room-scoped /sync as a background polling subscription
uxc subscribe start https://matrix.org/_matrix/client/v3 get:/sync \
--auth matrix-oauth \
--mode poll \
--poll-config '{"interval_secs":2,"extract_items_pointer":"/rooms/join/!abc123:example.org/timeline/events","missing_extract_items_pointer_as_empty":true,"request_cursor_arg":"since","response_cursor_pointer":"/next_batch","checkpoint_strategy":{"type":"cursor_only"}}' \
--sink file:$HOME/.uxc/subscriptions/matrix-sync.ndjson \
timeout=1000 \
'filter={"room":{"rooms":["!abc123:example.org"],"timeline":{"limit":5}}}'
# Inspect a user profile
matrix-openapi-cli get:/profile/{userId} userId=@alice:example.org
```
## Write Example (Confirm Intent First)
```bash
# Send a plain text room message with a unique transaction ID
matrix-openapi-cli put:/rooms/{roomId}/send/{eventType}/{txnId} \
'{"roomId":"!abc123:example.org","eventType":"m.room.message","txnId":"uxc-001","msgtype":"m.text","body":"Hello from UXC"}'
```
## Fallback Equivalence
- `matrix-openapi-cli <operation> ...` is equivalent to
`uxc https://matrix.org/_matrix/client/v3 --schema-url <matrix_openapi_schema> <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/matrix-openapi-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
USAGE_FILE="SKILL_DIR/references/usage-patterns.md"
SCHEMA_FILE="SKILL_DIR/references/matrix-client-server.openapi.json"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
for file in "SKILL_FILE" "OPENAI_FILE" "USAGE_FILE" "SCHEMA_FILE"; do
[[ -f "file" ]] || fail "missing required file: file"
done
rg -q '^name:\s*matrix-openapi-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v matrix-openapi-cli' "SKILL_FILE" || fail 'missing link-first command check'
rg -q 'uxc link matrix-openapi-cli https://matrix.org/_matrix/client/v3 --schema-url ' "SKILL_FILE" || fail 'missing fixed link create command with schema-url'
rg -q 'matrix-openapi-cli -h' "SKILL_FILE" || fail 'missing help-first host discovery example'
rg -q 'matrix-openapi-cli get:/account/whoami -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q -- '--auth-type bearer' "SKILL_FILE" || fail 'missing bearer auth setup'
rg -q 'uxc auth binding match https://matrix.org/_matrix/client/v3' "SKILL_FILE" || fail 'missing binding match check'
rg -q '/_matrix/client/v3' "SKILL_FILE" || fail 'missing homeserver base path guidance'
rg -q 'positional JSON' "SKILL_FILE" || fail 'missing positional JSON guidance'
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "USAGE_FILE"; then
fail 'found banned legacy JSON argument pattern'
fi
rg -q '^\s*display_name:\s*"Matrix Client-Server API"\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$matrix-openapi-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $matrix-openapi-skill'
echo "skills/matrix-openapi-skill validation passed"
Operate Slack Web API through UXC with a curated OpenAPI schema, bearer-token auth, and messaging-core guardrails.
---
name: slack-openapi-skill
description: Operate Slack Web API through UXC with a curated OpenAPI schema, bearer-token auth, and messaging-core guardrails.
---
# Slack Web API Skill
Use this skill to run Slack Web API operations through `uxc` + OpenAPI.
Reuse the `uxc` skill for shared execution, auth, and error-handling guidance.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://slack.com/api`.
- Access to the curated OpenAPI schema URL:
- `https://raw.githubusercontent.com/holon-run/uxc/main/skills/slack-openapi-skill/references/slack-web.openapi.json`
- A Slack bot token and, for selected thread/history reads, an optional user token.
## Scope
This skill covers a Messaging Core surface:
- auth validation
- channel lookup and inspection
- conversation history reads
- thread replies reads
- posting messages, including replies via `thread_ts`
- adding reactions
This skill does **not** cover:
- Slack OAuth app installation flow
- file upload flows
- `users.*`, `admin.*`, or `usergroups.*` method families
## Subscribe / Socket Mode Status
Slack inbound events can be delivered through Socket Mode. `uxc` now has a built-in Slack Socket Mode transport, but this skill still treats it as a limited event-ingest path rather than a fully packaged workflow surface.
Current `uxc subscribe` status:
- Slack Web API request/response calls are supported by this skill
- a live Socket Mode smoke test succeeded with the built-in transport:
- `uxc subscribe start https://slack.com/api --transport slack-socket-mode --auth slack-app --sink file:...`
- the runtime opened a fresh temporary WebSocket URL automatically
- the initial Slack `hello` frame was received
- a real inbound message event was validated end-to-end:
- while the Socket Mode job was running, a live Slack message event was delivered as an `events_api` envelope
- the sink recorded the message payload and `ack_sent=true`
What the current built-in transport already handles:
- app-level `xapp-...` auth via `--auth`
- automatic `apps.connections.open` before each connect attempt
- raw Socket Mode frame capture
- automatic ack for envelopes that carry `envelope_id`
What is still not packaged:
- event-shape guidance per subscribed Slack event family
- higher-level workflow packaging for common Slack event intake flows
Slack Socket Mode is now a validated IM subscribe provider at the transport/runtime level.
## Authentication
Slack Web API uses `Authorization: Bearer <token>`.
Token types used in practice:
- `xoxb-...`: Bot User OAuth Token. This is the recommended default for this skill.
- `xoxp-...`: User OAuth Token. Use this only when you explicitly want user-token semantics.
- `xapp-...`: App-level token. Use this for Socket Mode subscribe, not for normal Web API methods.
To create an app-level `xapp-...` token for Socket Mode:
1. Open the target Slack app at `https://api.slack.com/apps`
2. Go to `Basic Information`
3. Find `App-Level Tokens`
4. Generate a token with the `connections:write` scope
5. Enable `Socket Mode` in the app configuration before relying on subscribe-based event intake
### Option 1: Bot Token (Recommended Default)
Use the Slack `Bot User OAuth Token` (`xoxb-...`) for the default binding and for most messaging operations:
```bash
uxc auth credential set slack-bot \
--auth-type bearer \
--secret-env SLACK_BOT_TOKEN
uxc auth binding add \
--id slack-bot \
--host slack.com \
--path-prefix /api \
--scheme https \
--credential slack-bot \
--priority 100
```
### Option 2: User Token (Explicit Override For Selected Reads)
Use a separate Slack `User OAuth Token` (`xoxp-...`) when the method requires user-token semantics, especially thread/history access outside bot-accessible conversations:
```bash
uxc auth credential set slack-user \
--auth-type bearer \
--secret-env SLACK_USER_TOKEN
```
Do **not** bind `slack-user` by default to the same host/path. Invoke it explicitly when needed:
```bash
uxc auth binding match https://slack.com/api
slack-openapi-cli --auth slack-user get:/conversations.replies channel=C1234567890 ts=1717171717.000100
```
If you intentionally want writes to appear as the installing user rather than the bot, you can also invoke write methods with `--auth slack-user`, but treat that as an explicit override rather than the default path.
## Core Workflow
1. Use the fixed link command by default:
- `command -v slack-openapi-cli`
- If missing, create it:
`uxc link slack-openapi-cli https://slack.com/api --schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/slack-openapi-skill/references/slack-web.openapi.json`
- `slack-openapi-cli -h`
2. Inspect operation schema first:
- `slack-openapi-cli get:/auth.test -h`
- `slack-openapi-cli get:/conversations.history -h`
- `slack-openapi-cli post:/chat.postMessage -h`
3. Prefer read validation before writes:
- `slack-openapi-cli get:/auth.test`
- `slack-openapi-cli get:/conversations.list limit=20 types=public_channel,private_channel`
- `slack-openapi-cli get:/conversations.info channel=C1234567890`
4. Execute with key/value or positional JSON:
- key/value:
`slack-openapi-cli get:/conversations.history channel=C1234567890 limit=20`
- positional JSON:
`slack-openapi-cli post:/chat.postMessage '{"channel":"C1234567890","text":"Hello from UXC"}'`
## Operation Groups
### Read / Lookup
- `get:/auth.test`
- `get:/conversations.list`
- `get:/conversations.info`
- `get:/conversations.history`
- `get:/conversations.replies`
### Messaging / Reactions
- `post:/chat.postMessage`
- `post:/reactions.add`
## Guardrails
- Keep automation on the JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Bot token is the recommended default for send and basic read flows.
- Bot token means Slack `Bot User OAuth Token` (`xoxb-...`); do not confuse it with `xapp-...` app-level tokens.
- User token means Slack `User OAuth Token` (`xoxp-...`); use `--auth slack-user` when you intentionally need user identity or user-token-only reads.
- `get:/conversations.replies` has token-type restrictions:
- bot token works for IM and MPIM threads the bot can access
- public/private channel thread reads should use `--auth slack-user`
- `get:/conversations.history` only returns conversations visible to the supplied token; a bot token is limited to joined conversations.
- Slack rate limits for `conversations.history` and `conversations.replies` vary by app distribution. Slack documents a tighter limit for newly created commercially distributed non-Marketplace apps starting on May 29, 2025; do not assume generic Tier 3 behavior.
- Treat `post:/chat.postMessage` and `post:/reactions.add` as write/high-risk operations; require explicit user confirmation before execution.
- `slack-openapi-cli <operation> ...` is equivalent to `uxc https://slack.com/api --schema-url <slack_openapi_schema> <operation> ...`.
## References
- Usage patterns: `references/usage-patterns.md`
- Curated OpenAPI schema: `references/slack-web.openapi.json`
- Slack Web API docs: https://docs.slack.dev/apis/web-api
- `chat.postMessage`: https://docs.slack.dev/reference/methods/chat.postMessage
- `conversations.history`: https://docs.slack.dev/reference/methods/conversations.history
- `conversations.replies`: https://docs.slack.dev/reference/methods/conversations.replies/
FILE:agents/openai.yaml
interface:
display_name: "Slack Web API"
short_description: "Operate Slack Web API via UXC + curated OpenAPI schema"
default_prompt: "Use $slack-openapi-skill to discover and execute Slack Web API operations through UXC with bearer-token auth, messaging-core scope, and bot/user token guardrails."
FILE:references/slack-web.openapi.json
{
"openapi": "3.0.3",
"info": {
"title": "Slack Web API (Curated)",
"version": "1.0.0",
"description": "Curated Slack Web API schema for common messaging workflows in UXC."
},
"servers": [
{
"url": "https://slack.com/api"
}
],
"security": [
{
"bearerAuth": []
}
],
"paths": {
"/auth.test": {
"get": {
"operationId": "get:/auth.test",
"summary": "Validate the active token and return workspace identity.",
"description": "Checks authentication and basic identity details for the active token.",
"responses": {
"200": {
"description": "Authentication test response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/AuthTestSuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"auth"
]
}
},
"/conversations.list": {
"get": {
"operationId": "get:/conversations.list",
"summary": "List channels and conversations visible to the token.",
"parameters": [
{
"name": "exclude_archived",
"in": "query",
"description": "Exclude archived channels from the result set.",
"schema": {
"type": "boolean"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of conversations to return.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 999
}
},
{
"name": "cursor",
"in": "query",
"description": "Pagination cursor returned by a previous call.",
"schema": {
"type": "string"
}
},
{
"name": "team_id",
"in": "query",
"description": "Workspace ID for org-level tokens when required.",
"schema": {
"type": "string"
}
},
{
"name": "types",
"in": "query",
"description": "Comma-separated conversation types such as public_channel, private_channel, mpim, im.",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Conversation list response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ConversationsListSuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"conversations"
]
}
},
"/conversations.info": {
"get": {
"operationId": "get:/conversations.info",
"summary": "Fetch metadata for a conversation by ID.",
"parameters": [
{
"name": "channel",
"in": "query",
"required": true,
"description": "Conversation ID such as C123 or G123.",
"schema": {
"type": "string"
}
},
{
"name": "include_locale",
"in": "query",
"description": "Include locale details when available.",
"schema": {
"type": "boolean"
}
},
{
"name": "include_num_members",
"in": "query",
"description": "Include member count when available.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "Conversation info response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ConversationInfoSuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"conversations"
]
}
},
"/conversations.history": {
"get": {
"operationId": "get:/conversations.history",
"summary": "Read message history from a conversation.",
"parameters": [
{
"name": "channel",
"in": "query",
"required": true,
"description": "Conversation ID to fetch history for.",
"schema": {
"type": "string"
}
},
{
"name": "latest",
"in": "query",
"description": "End of the time range to include.",
"schema": {
"type": "string"
}
},
{
"name": "oldest",
"in": "query",
"description": "Start of the time range to include.",
"schema": {
"type": "string"
}
},
{
"name": "inclusive",
"in": "query",
"description": "Include messages matching latest or oldest exactly.",
"schema": {
"type": "boolean"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of messages to return.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 999
}
},
{
"name": "cursor",
"in": "query",
"description": "Pagination cursor returned by a previous call.",
"schema": {
"type": "string"
}
},
{
"name": "include_all_metadata",
"in": "query",
"description": "Request additional metadata when available.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "Conversation history response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ConversationHistorySuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"conversations"
]
}
},
"/conversations.replies": {
"get": {
"operationId": "get:/conversations.replies",
"summary": "Read replies in a thread.",
"parameters": [
{
"name": "channel",
"in": "query",
"required": true,
"description": "Conversation ID that contains the parent message.",
"schema": {
"type": "string"
}
},
{
"name": "ts",
"in": "query",
"required": true,
"description": "Parent message timestamp for the thread root.",
"schema": {
"type": "string"
}
},
{
"name": "latest",
"in": "query",
"description": "End of the time range to include.",
"schema": {
"type": "string"
}
},
{
"name": "oldest",
"in": "query",
"description": "Start of the time range to include.",
"schema": {
"type": "string"
}
},
{
"name": "inclusive",
"in": "query",
"description": "Include messages matching latest or oldest exactly.",
"schema": {
"type": "boolean"
}
},
{
"name": "limit",
"in": "query",
"description": "Maximum number of replies to return.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 999
}
},
{
"name": "cursor",
"in": "query",
"description": "Pagination cursor returned by a previous call.",
"schema": {
"type": "string"
}
},
{
"name": "include_all_metadata",
"in": "query",
"description": "Request additional metadata when available.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"200": {
"description": "Conversation replies response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ConversationRepliesSuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"conversations"
]
}
},
"/chat.postMessage": {
"post": {
"operationId": "post:/chat.postMessage",
"summary": "Post a message to a channel or thread.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatPostMessageRequest"
}
}
}
},
"responses": {
"200": {
"description": "Message post response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/ChatPostMessageSuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"chat"
]
}
},
"/reactions.add": {
"post": {
"operationId": "post:/reactions.add",
"summary": "Add an emoji reaction to a message.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ReactionsAddRequest"
}
}
}
},
"responses": {
"200": {
"description": "Reaction add response",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/OkOnlySuccess"
},
{
"$ref": "#/components/schemas/ErrorResponse"
}
]
}
}
}
}
},
"tags": [
"reactions"
]
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "Slack token"
}
},
"schemas": {
"ErrorResponse": {
"type": "object",
"required": [
"ok",
"error"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
false
]
},
"error": {
"type": "string",
"description": "Slack error code such as invalid_auth, channel_not_found, or missing_scope."
},
"needed": {
"type": "string",
"description": "Scope needed when the failure is scope-related."
},
"provided": {
"type": "string",
"description": "Scope set currently present on the token when reported."
},
"warning": {
"type": "string"
}
},
"additionalProperties": true
},
"OkOnlySuccess": {
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
}
},
"additionalProperties": true
},
"ResponseMetadata": {
"type": "object",
"properties": {
"next_cursor": {
"type": "string"
}
},
"additionalProperties": true
},
"ConversationTopic": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"creator": {
"type": "string"
},
"last_set": {
"type": "integer"
}
},
"additionalProperties": true
},
"ConversationPurpose": {
"type": "object",
"properties": {
"value": {
"type": "string"
},
"creator": {
"type": "string"
},
"last_set": {
"type": "integer"
}
},
"additionalProperties": true
},
"Conversation": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"is_channel": {
"type": "boolean"
},
"is_group": {
"type": "boolean"
},
"is_im": {
"type": "boolean"
},
"is_mpim": {
"type": "boolean"
},
"is_private": {
"type": "boolean"
},
"is_archived": {
"type": "boolean"
},
"is_member": {
"type": "boolean"
},
"is_general": {
"type": "boolean"
},
"num_members": {
"type": "integer"
},
"user": {
"type": "string"
},
"topic": {
"$ref": "#/components/schemas/ConversationTopic"
},
"purpose": {
"$ref": "#/components/schemas/ConversationPurpose"
}
},
"additionalProperties": true
},
"BlockTextObject": {
"type": "object",
"required": [
"type",
"text"
],
"properties": {
"type": {
"type": "string",
"enum": [
"plain_text",
"mrkdwn"
]
},
"text": {
"type": "string"
},
"emoji": {
"type": "boolean"
},
"verbatim": {
"type": "boolean"
}
},
"additionalProperties": true
},
"BlockElement": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"text": {
"$ref": "#/components/schemas/BlockTextObject"
}
},
"additionalProperties": true
},
"BlockObject": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"block_id": {
"type": "string"
},
"text": {
"$ref": "#/components/schemas/BlockTextObject"
},
"fields": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BlockTextObject"
}
},
"elements": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BlockElement"
}
}
},
"additionalProperties": true
},
"AttachmentField": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"value": {
"type": "string"
},
"short": {
"type": "boolean"
}
},
"additionalProperties": true
},
"AttachmentObject": {
"type": "object",
"properties": {
"fallback": {
"type": "string"
},
"color": {
"type": "string"
},
"pretext": {
"type": "string"
},
"author_name": {
"type": "string"
},
"title": {
"type": "string"
},
"text": {
"type": "string"
},
"footer": {
"type": "string"
},
"fields": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AttachmentField"
}
}
},
"additionalProperties": true
},
"Message": {
"type": "object",
"properties": {
"type": {
"type": "string"
},
"user": {
"type": "string"
},
"bot_id": {
"type": "string"
},
"subtype": {
"type": "string"
},
"text": {
"type": "string"
},
"ts": {
"type": "string"
},
"thread_ts": {
"type": "string"
},
"reply_count": {
"type": "integer"
},
"reply_users_count": {
"type": "integer"
},
"latest_reply": {
"type": "string"
},
"blocks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BlockObject"
}
},
"attachments": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AttachmentObject"
}
}
},
"additionalProperties": true
},
"AuthTestSuccess": {
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"url": {
"type": "string"
},
"team": {
"type": "string"
},
"user": {
"type": "string"
},
"team_id": {
"type": "string"
},
"user_id": {
"type": "string"
},
"bot_id": {
"type": "string"
},
"enterprise_id": {
"type": "string"
},
"is_enterprise_install": {
"type": "boolean"
}
},
"additionalProperties": true
},
"ConversationsListSuccess": {
"type": "object",
"required": [
"ok",
"channels"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"channels": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Conversation"
}
},
"response_metadata": {
"$ref": "#/components/schemas/ResponseMetadata"
}
},
"additionalProperties": true
},
"ConversationInfoSuccess": {
"type": "object",
"required": [
"ok",
"channel"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"channel": {
"$ref": "#/components/schemas/Conversation"
}
},
"additionalProperties": true
},
"ConversationHistorySuccess": {
"type": "object",
"required": [
"ok",
"messages"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"messages": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Message"
}
},
"has_more": {
"type": "boolean"
},
"pin_count": {
"type": "integer"
},
"response_metadata": {
"$ref": "#/components/schemas/ResponseMetadata"
}
},
"additionalProperties": true
},
"ConversationRepliesSuccess": {
"type": "object",
"required": [
"ok",
"messages"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"messages": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Message"
}
},
"has_more": {
"type": "boolean"
},
"response_metadata": {
"$ref": "#/components/schemas/ResponseMetadata"
}
},
"additionalProperties": true
},
"ChatPostMessageRequest": {
"type": "object",
"required": [
"channel"
],
"properties": {
"channel": {
"type": "string",
"description": "Conversation ID where the message will be posted."
},
"text": {
"type": "string",
"description": "Fallback text and message body for simple messages."
},
"blocks": {
"type": "array",
"items": {
"$ref": "#/components/schemas/BlockObject"
}
},
"attachments": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AttachmentObject"
}
},
"thread_ts": {
"type": "string",
"description": "Parent message timestamp to post into a thread."
},
"reply_broadcast": {
"type": "boolean",
"description": "Broadcast a threaded reply into the channel timeline."
},
"mrkdwn": {
"type": "boolean"
},
"unfurl_links": {
"type": "boolean"
},
"unfurl_media": {
"type": "boolean"
},
"metadata": {
"type": "object",
"description": "Optional Slack metadata object.",
"additionalProperties": true
}
},
"additionalProperties": false
},
"ChatPostMessageSuccess": {
"type": "object",
"required": [
"ok",
"channel",
"ts",
"message"
],
"properties": {
"ok": {
"type": "boolean",
"enum": [
true
]
},
"channel": {
"type": "string"
},
"ts": {
"type": "string"
},
"message": {
"$ref": "#/components/schemas/Message"
}
},
"additionalProperties": true
},
"ReactionsAddRequest": {
"type": "object",
"required": [
"channel",
"name",
"timestamp"
],
"properties": {
"channel": {
"type": "string",
"description": "Conversation ID that contains the target message."
},
"name": {
"type": "string",
"description": "Emoji name without surrounding colons."
},
"timestamp": {
"type": "string",
"description": "Timestamp of the message receiving the reaction."
}
},
"additionalProperties": false
}
}
}
}
FILE:references/usage-patterns.md
# Slack Web API Skill - Usage Patterns
## Link Setup
```bash
command -v slack-openapi-cli
uxc link slack-openapi-cli https://slack.com/api \
--schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/slack-openapi-skill/references/slack-web.openapi.json
slack-openapi-cli -h
```
## Auth Setup (Bot Token Default)
Use the Slack `Bot User OAuth Token` (`xoxb-...`) here.
```bash
uxc auth credential set slack-bot \
--auth-type bearer \
--secret-env SLACK_BOT_TOKEN
uxc auth binding add \
--id slack-bot \
--host slack.com \
--path-prefix /api \
--scheme https \
--credential slack-bot \
--priority 100
```
## Auth Setup (User Token Override)
Use the Slack `User OAuth Token` (`xoxp-...`) here. Do not use `xapp-...` app-level tokens with this skill.
```bash
uxc auth credential set slack-user \
--auth-type bearer \
--secret-env SLACK_USER_TOKEN
```
Use `--auth slack-user` for reads that need user-token semantics:
```bash
slack-openapi-cli --auth slack-user get:/conversations.replies channel=C1234567890 ts=1717171717.000100
slack-openapi-cli --auth slack-user get:/conversations.history channel=C1234567890 limit=50
```
Use `--auth slack-user` on write methods only when you intentionally want the message to be attributed to the user-token identity rather than the default bot path.
## Auth Setup (Socket Mode App Token)
Use an app-level `xapp-...` token for Socket Mode subscriptions:
- Slack app page: `https://api.slack.com/apps`
- Path: `Basic Information -> App-Level Tokens`
- Recommended scope: `connections:write`
```bash
uxc auth credential set slack-app \
--auth-type bearer \
--secret-env SLACK_APP_TOKEN
```
Invoke it explicitly with `--auth slack-app` when using `--transport slack-socket-mode`.
## Read Examples
```bash
# Confirm token identity and workspace
slack-openapi-cli get:/auth.test
# List candidate channels
slack-openapi-cli get:/conversations.list limit=20 types=public_channel,private_channel
# Inspect one conversation
slack-openapi-cli get:/conversations.info channel=C1234567890
# Read recent history
slack-openapi-cli get:/conversations.history channel=C1234567890 limit=20
# Read thread replies
slack-openapi-cli --auth slack-user get:/conversations.replies channel=C1234567890 ts=1717171717.000100
```
## Write Examples (Confirm Intent First)
```bash
# Post a channel message
slack-openapi-cli post:/chat.postMessage '{"channel":"C1234567890","text":"Hello from UXC"}'
# Post a thread reply
slack-openapi-cli post:/chat.postMessage '{"channel":"C1234567890","text":"Reply from UXC","thread_ts":"1717171717.000100"}'
# Add a reaction to a message
slack-openapi-cli post:/reactions.add '{"channel":"C1234567890","timestamp":"1717171717.000100","name":"thumbsup"}'
```
## Socket Mode Subscribe
```bash
# Start Slack Socket Mode using an app-level xapp token.
# The runtime will call apps.connections.open automatically.
uxc subscribe start https://slack.com/api \
--transport slack-socket-mode \
--auth slack-app \
--sink file:$HOME/.uxc/subscriptions/slack-socket-mode.ndjson
# Inspect job state
uxc subscribe list
uxc subscribe status sub_123
# Stop the background job
uxc subscribe stop sub_123
```
Current validation level:
- live connection/hello path validated
- automatic temporary WebSocket URL acquisition validated
- automatic ack for Socket Mode envelopes is implemented
- live `events_api` message delivery has been validated against a real Slack workspace
## Fallback Equivalence
- `slack-openapi-cli <operation> ...` is equivalent to
`uxc https://slack.com/api --schema-url <slack_openapi_schema> <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/slack-openapi-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
USAGE_FILE="SKILL_DIR/references/usage-patterns.md"
SCHEMA_FILE="SKILL_DIR/references/slack-web.openapi.json"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
need_cmd jq
for file in "SKILL_FILE" "OPENAI_FILE" "USAGE_FILE" "SCHEMA_FILE"; do
[[ -f "file" ]] || fail "missing required file: file"
done
# Sanity-check the Slack Web API OpenAPI schema
jq -e '.openapi and .paths' "SCHEMA_FILE" >/dev/null 2>&1 || fail 'invalid OpenAPI schema JSON or missing .openapi/.paths'
jq -e '.paths["/auth.test"]' "SCHEMA_FILE" >/dev/null 2>&1 || fail 'OpenAPI schema missing /auth.test path'
rg -q '^name:\s*slack-openapi-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v slack-openapi-cli' "SKILL_FILE" || fail 'missing link-first command check'
rg -q 'uxc link slack-openapi-cli https://slack.com/api --schema-url ' "SKILL_FILE" || fail 'missing fixed link create command with schema-url'
rg -q 'slack-openapi-cli -h' "SKILL_FILE" || fail 'missing help-first host discovery example'
rg -q 'slack-openapi-cli get:/auth.test -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q -- '--auth-type bearer' "SKILL_FILE" || fail 'missing bearer auth setup'
rg -q 'uxc auth binding match https://slack.com/api' "SKILL_FILE" || fail 'missing binding match check'
rg -q 'positional JSON' "SKILL_FILE" || fail 'missing positional JSON guidance'
rg -q 'May 29, 2025' "SKILL_FILE" || fail 'missing rate limit guardrail date'
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "USAGE_FILE"; then
fail 'found banned legacy JSON argument pattern'
fi
rg -q '^\s*display_name:\s*"Slack Web API"\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$slack-openapi-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $slack-openapi-skill'
echo "skills/slack-openapi-skill validation passed"
Operate Binance Web3 public market and research APIs through UXC with a curated OpenAPI schema. Use when tasks need token search, token metadata/market snaps...
---
name: binance-web3-openapi-skill
description: Operate Binance Web3 public market and research APIs through UXC with a curated OpenAPI schema. Use when tasks need token search, token metadata/market snapshots, address holdings, rankings, token audit, or smart money signals on Binance Web3.
---
# Binance Web3 API Skill
Use this skill to run Binance Web3 public read operations through `uxc` + OpenAPI.
Reuse the `uxc` skill for shared execution and error-handling guidance.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://web3.binance.com`.
- Access to the curated OpenAPI schema URL:
- `https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-web3-openapi-skill/references/binance-web3.openapi.json`
## Scope
This skill covers the public `web3.binance.com` endpoints for:
- token search
- token metadata
- token market snapshots
- address holdings
- token security audit
- social hype leaderboard
- unified token ranks
- meme rush ranks
- smart money signals
This skill does **not** cover:
- Binance Spot / account trading APIs
- Binance Square posting
- K-line candles hosted on `https://dquery.sintral.io`
## Authentication
Most operations are public and do not require API credentials.
## Core Workflow
1. Use the fixed link command by default:
- `command -v binance-web3-openapi-cli`
- If missing, create it:
`uxc link binance-web3-openapi-cli https://web3.binance.com --schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-web3-openapi-skill/references/binance-web3.openapi.json`
- `binance-web3-openapi-cli -h`
2. Inspect operation schema first:
- `binance-web3-openapi-cli get:/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search -h`
- `binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/security/token/audit -h`
3. Execute operation:
- key/value:
`binance-web3-openapi-cli get:/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search keyword=bnb chainIds=56 orderBy=volume24h`
- positional JSON:
`binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/unified/rank/list '{"rankType":10,"chainId":"56","period":50,"page":1,"size":20}'`
## Operation Groups
### Token Discovery And Market Snapshot
- Search tokens:
- `get:/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search`
- Fetch token metadata:
- `get:/bapi/defi/v1/public/wallet-direct/buw/wallet/dex/market/token/meta/info`
- Fetch token market data:
- `get:/bapi/defi/v4/public/wallet-direct/buw/wallet/market/token/dynamic/info`
### Rankings And Signals
- Social hype leaderboard:
- `get:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/social/hype/rank/leaderboard`
- Unified token rank:
- `post:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/unified/rank/list`
- Meme rush rank:
- `post:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/rank/list`
- Smart money signals:
- `post:/bapi/defi/v1/public/wallet-direct/buw/wallet/web/signal/smart-money`
### Research
- Address holdings:
- `get:/bapi/defi/v3/public/wallet-direct/buw/wallet/address/pnl/active-position-list`
- Token audit:
- `post:/bapi/defi/v1/public/wallet-direct/security/token/audit`
## Guardrails
- Keep automation on the JSON output envelope; do not use `--text`.
- Parse stable envelope fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Binance Web3 responses usually wrap payloads as `code`, `message`, `success`, `data`; treat `code == "000000"` as success.
- `audit` requires a UUID v4 `requestId`; generate one for every request instead of reusing old IDs.
- Address holdings requires operation-level headers `clienttype=web` and `clientversion=1.2.0`; keep them scoped to that operation instead of injecting them host-wide.
- For non-string objects, prefer positional JSON instead of flattening complex filters into many `key=value` args.
- `binance-web3-openapi-cli <operation> ...` is equivalent to `uxc https://web3.binance.com --schema-url <binance_web3_openapi_schema> <operation> ...`.
## References
- Usage patterns: `references/usage-patterns.md`
- Curated OpenAPI schema: `references/binance-web3.openapi.json`
- Binance skills hub source material: https://github.com/binance/binance-skills-hub/tree/main/skills/binance-web3
FILE:agents/openai.yaml
interface:
display_name: "Binance Web3 API"
short_description: "Operate Binance Web3 public token, address holdings, ranking, audit, and signal APIs via UXC + OpenAPI schema mapping"
default_prompt: "Use $binance-web3-openapi-skill to discover and execute Binance Web3 public read operations through UXC with the curated OpenAPI schema and operation-level headers where required."
FILE:references/binance-web3.openapi.json
{
"openapi": "3.1.0",
"info": {
"title": "Binance Web3 Public API",
"version": "1.0.0",
"description": "Curated OpenAPI schema for selected public Binance Web3 endpoints on web3.binance.com."
},
"servers": [
{
"url": "https://web3.binance.com"
}
],
"paths": {
"/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search": {
"get": {
"summary": "Search tokens",
"description": "Search tokens by keyword, optionally filtered by chain IDs and sort order.",
"parameters": [
{
"name": "keyword",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "chainIds",
"in": "query",
"required": false,
"schema": {
"type": "string",
"description": "Comma-separated Binance chain IDs such as 56,8453,CT_501."
}
},
{
"name": "orderBy",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Token search results",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenSearchEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/buw/wallet/dex/market/token/meta/info": {
"get": {
"summary": "Get token metadata",
"description": "Fetch token metadata, social links, creator address, and description.",
"parameters": [
{
"name": "chainId",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "contractAddress",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Token metadata",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenMetadataEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v4/public/wallet-direct/buw/wallet/market/token/dynamic/info": {
"get": {
"summary": "Get token market data",
"description": "Fetch real-time token market data such as price, volume, holder counts, and liquidity.",
"parameters": [
{
"name": "chainId",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "contractAddress",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Token market data",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenMarketEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v3/public/wallet-direct/buw/wallet/address/pnl/active-position-list": {
"get": {
"summary": "List address active positions",
"description": "Fetch active token positions for a wallet address. This endpoint requires operation-level browser-style headers and should not be modeled as host-wide defaults.",
"parameters": [
{
"name": "address",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "chainId",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 0
}
},
{
"name": "clienttype",
"in": "header",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "clientversion",
"in": "header",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Address active positions",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AddressPositionsEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/security/token/audit": {
"post": {
"summary": "Audit token security",
"description": "Run Binance Web3 token security checks such as contract risk, trading risk, and scam risk.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenAuditRequest"
}
}
}
},
"responses": {
"200": {
"description": "Token audit result",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenAuditEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/social/hype/rank/leaderboard": {
"get": {
"summary": "Get social hype leaderboard",
"description": "Retrieve Binance Web3 social hype leaderboard entries by chain and time range.",
"parameters": [
{
"name": "chainId",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "targetLanguage",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "timeRange",
"in": "query",
"required": true,
"schema": {
"type": "integer"
}
},
{
"name": "sentiment",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": [
"All",
"Positive",
"Negative",
"Neutral"
]
}
},
{
"name": "socialLanguage",
"in": "query",
"required": false,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Social hype leaderboard",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SocialHypeLeaderboardEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/unified/rank/list": {
"post": {
"summary": "List unified token rank",
"description": "Query trending, top search, alpha, or stock token rankings.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnifiedRankRequest"
}
}
}
},
"responses": {
"200": {
"description": "Unified token ranking",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnifiedRankEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/rank/list": {
"post": {
"summary": "List meme rush rank",
"description": "Query meme token launchpad rankings such as New, Finalizing, and Migrated.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MemeRushRequest"
}
}
}
},
"responses": {
"200": {
"description": "Meme rush ranking",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MemeRushEnvelope"
}
}
}
}
}
}
},
"/bapi/defi/v1/public/wallet-direct/buw/wallet/web/signal/smart-money": {
"post": {
"summary": "List smart money signals",
"description": "Retrieve Binance Web3 smart money buy and sell signals.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SmartMoneySignalsRequest"
}
}
}
},
"responses": {
"200": {
"description": "Smart money signals",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SmartMoneySignalsEnvelope"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"EnvelopeBase": {
"type": "object",
"properties": {
"code": {
"type": [
"string",
"null"
]
},
"message": {
"type": [
"string",
"null"
]
},
"messageDetail": {
"type": [
"string",
"null"
]
},
"success": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true
},
"LinkItem": {
"type": "object",
"properties": {
"label": {
"type": [
"string",
"null"
]
},
"link": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"TokenSearchItem": {
"type": "object",
"properties": {
"chainId": {
"type": "string"
},
"contractAddress": {
"type": [
"string",
"null"
]
},
"tokenId": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"symbol": {
"type": [
"string",
"null"
]
},
"icon": {
"type": [
"string",
"null"
]
},
"price": {
"type": [
"string",
"null"
]
},
"percentChange24h": {
"type": [
"string",
"null"
]
},
"volume24h": {
"type": [
"string",
"null"
]
},
"marketCap": {
"type": [
"string",
"null"
]
},
"liquidity": {
"type": [
"string",
"null"
]
},
"links": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LinkItem"
}
}
},
"additionalProperties": true
},
"TokenSearchEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TokenSearchItem"
}
}
}
}
]
},
"TokenMetadata": {
"type": "object",
"properties": {
"tokenId": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"symbol": {
"type": [
"string",
"null"
]
},
"chainId": {
"type": [
"string",
"null"
]
},
"chainName": {
"type": [
"string",
"null"
]
},
"contractAddress": {
"type": [
"string",
"null"
]
},
"decimals": {
"type": [
"integer",
"null"
]
},
"icon": {
"type": [
"string",
"null"
]
},
"creatorAddress": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"links": {
"type": "array",
"items": {
"$ref": "#/components/schemas/LinkItem"
}
}
},
"additionalProperties": true
},
"TokenMetadataEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/TokenMetadata"
}
}
}
]
},
"TokenMarketData": {
"type": "object",
"properties": {
"price": {
"type": [
"string",
"null"
]
},
"nativeTokenPrice": {
"type": [
"string",
"null"
]
},
"volume24h": {
"type": [
"string",
"null"
]
},
"percentChange5m": {
"type": [
"string",
"null"
]
},
"percentChange1h": {
"type": [
"string",
"null"
]
},
"percentChange4h": {
"type": [
"string",
"null"
]
},
"percentChange24h": {
"type": [
"string",
"null"
]
},
"marketCap": {
"type": [
"string",
"null"
]
},
"fdv": {
"type": [
"string",
"null"
]
},
"liquidity": {
"type": [
"string",
"null"
]
},
"holders": {
"type": [
"string",
"null"
]
},
"top10HoldersPercentage": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"TokenMarketEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/TokenMarketData"
}
}
}
]
},
"AddressPositionItem": {
"type": "object",
"properties": {
"chainId": {
"type": [
"string",
"null"
]
},
"address": {
"type": [
"string",
"null"
]
},
"contractAddress": {
"type": [
"string",
"null"
]
},
"tokenId": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"symbol": {
"type": [
"string",
"null"
]
},
"icon": {
"type": [
"string",
"null"
]
},
"price": {
"type": [
"string",
"null"
]
},
"percentChange24h": {
"type": [
"string",
"null"
]
},
"remainQty": {
"type": [
"string",
"null"
]
},
"costAmount": {
"type": [
"string",
"null"
]
},
"value": {
"type": [
"string",
"null"
]
},
"pnlAmount": {
"type": [
"string",
"null"
]
},
"pnlPercent": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"AddressPositionsData": {
"type": "object",
"properties": {
"offset": {
"type": [
"integer",
"null"
]
},
"addressStatus": {
"type": [
"string",
"null"
]
},
"list": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/components/schemas/AddressPositionItem"
}
}
},
"additionalProperties": true
},
"AddressPositionsEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/AddressPositionsData"
}
}
}
]
},
"TokenAuditRequest": {
"type": "object",
"required": [
"binanceChainId",
"contractAddress",
"requestId"
],
"properties": {
"binanceChainId": {
"type": "string"
},
"contractAddress": {
"type": "string"
},
"requestId": {
"type": "string",
"format": "uuid"
}
},
"additionalProperties": false
},
"RiskItemDetail": {
"type": "object",
"properties": {
"title": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"isHit": {
"type": [
"boolean",
"null"
]
},
"riskType": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"RiskItem": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"details": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RiskItemDetail"
}
}
},
"additionalProperties": true
},
"TokenAuditData": {
"type": "object",
"properties": {
"requestId": {
"type": [
"string",
"null"
]
},
"hasResult": {
"type": [
"boolean",
"null"
]
},
"isSupported": {
"type": [
"boolean",
"null"
]
},
"riskLevelEnum": {
"type": [
"string",
"null"
]
},
"riskLevel": {
"type": [
"integer",
"null"
]
},
"extraInfo": {
"type": [
"object",
"null"
],
"additionalProperties": true
},
"riskItems": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RiskItem"
}
}
},
"additionalProperties": true
},
"TokenAuditEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/TokenAuditData"
}
}
}
]
},
"SocialHypeLeaderboardItem": {
"type": "object",
"properties": {
"metaInfo": {
"type": [
"object",
"null"
],
"additionalProperties": true
},
"marketInfo": {
"type": [
"object",
"null"
],
"additionalProperties": true
},
"socialHypeInfo": {
"type": [
"object",
"null"
],
"additionalProperties": true
}
},
"additionalProperties": true
},
"SocialHypeLeaderboardData": {
"type": "object",
"properties": {
"leaderBoardList": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SocialHypeLeaderboardItem"
}
}
},
"additionalProperties": true
},
"SocialHypeLeaderboardEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/SocialHypeLeaderboardData"
}
}
}
]
},
"UnifiedRankRequest": {
"type": "object",
"properties": {
"rankType": {
"type": "integer",
"enum": [
10,
11,
20,
40
]
},
"chainId": {
"type": "string"
},
"period": {
"type": "integer"
},
"sortBy": {
"type": "integer"
},
"orderAsc": {
"type": "boolean"
},
"page": {
"type": "integer",
"minimum": 1
},
"size": {
"type": "integer",
"minimum": 1,
"maximum": 200
}
},
"additionalProperties": true
},
"RankTokenItem": {
"type": "object",
"properties": {
"chainId": {
"type": [
"string",
"null"
]
},
"contractAddress": {
"type": [
"string",
"null"
]
},
"symbol": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"icon": {
"type": [
"string",
"null"
]
},
"price": {
"type": [
"string",
"null"
]
},
"marketCap": {
"type": [
"string",
"null"
]
},
"liquidity": {
"type": [
"string",
"null"
]
},
"holders": {
"type": [
"string",
"null"
]
},
"launchTime": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"UnifiedRankData": {
"type": "object",
"properties": {
"tokens": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RankTokenItem"
}
},
"total": {
"type": [
"integer",
"null"
]
},
"page": {
"type": [
"integer",
"null"
]
},
"size": {
"type": [
"integer",
"null"
]
}
},
"additionalProperties": true
},
"UnifiedRankEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/UnifiedRankData"
}
}
}
]
},
"MemeRushRequest": {
"type": "object",
"required": [
"chainId",
"rankType"
],
"properties": {
"chainId": {
"type": "string"
},
"rankType": {
"type": "integer",
"enum": [
10,
20,
30
]
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 200
},
"keywords": {
"type": "array",
"items": {
"type": "string"
}
},
"excludes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": true
},
"MemeRushData": {
"type": "object",
"properties": {
"tokens": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RankTokenItem"
}
},
"total": {
"type": [
"integer",
"null"
]
}
},
"additionalProperties": true
},
"MemeRushEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/components/schemas/MemeRushData"
}
}
}
]
},
"SmartMoneySignalsRequest": {
"type": "object",
"required": [
"page",
"pageSize",
"chainId"
],
"properties": {
"smartSignalType": {
"type": "string"
},
"page": {
"type": "integer",
"minimum": 1
},
"pageSize": {
"type": "integer",
"minimum": 1,
"maximum": 100
},
"chainId": {
"type": "string"
}
},
"additionalProperties": true
},
"SmartMoneySignalItem": {
"type": "object",
"properties": {
"signalId": {
"type": [
"integer",
"null"
]
},
"ticker": {
"type": [
"string",
"null"
]
},
"chainId": {
"type": [
"string",
"null"
]
},
"contractAddress": {
"type": [
"string",
"null"
]
},
"smartSignalType": {
"type": [
"string",
"null"
]
},
"smartMoneyCount": {
"type": [
"integer",
"null"
]
},
"direction": {
"type": [
"string",
"null"
]
},
"signalTriggerTime": {
"type": [
"integer",
"null"
]
},
"alertPrice": {
"type": [
"string",
"null"
]
},
"currentPrice": {
"type": [
"string",
"null"
]
},
"maxGain": {
"type": [
"string",
"null"
]
},
"exitRate": {
"type": [
"integer",
"null"
]
},
"status": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
},
"SmartMoneySignalsEnvelope": {
"allOf": [
{
"$ref": "#/components/schemas/EnvelopeBase"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SmartMoneySignalItem"
}
}
}
}
]
}
}
}
}
FILE:references/usage-patterns.md
# Binance Web3 API Skill - Usage Patterns
## Link Setup
```bash
command -v binance-web3-openapi-cli
uxc link binance-web3-openapi-cli https://web3.binance.com \
--schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-web3-openapi-skill/references/binance-web3.openapi.json
binance-web3-openapi-cli -h
```
## Read Examples
```bash
# Search tokens by keyword
binance-web3-openapi-cli get:/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search \
keyword=bnb chainIds=56 orderBy=volume24h
# Get token metadata
binance-web3-openapi-cli get:/bapi/defi/v1/public/wallet-direct/buw/wallet/dex/market/token/meta/info \
chainId=56 contractAddress=0x55d398326f99059ff775485246999027b3197955
# Get token market snapshot
binance-web3-openapi-cli get:/bapi/defi/v4/public/wallet-direct/buw/wallet/market/token/dynamic/info \
chainId=56 contractAddress=0x55d398326f99059ff775485246999027b3197955
# List active positions for an address
binance-web3-openapi-cli get:/bapi/defi/v3/public/wallet-direct/buw/wallet/address/pnl/active-position-list \
address=0x0000000000000000000000000000000000000001 chainId=56 offset=0 \
clienttype=web clientversion=1.2.0
```
## Ranking And Signal Examples
```bash
# Social hype leaderboard
binance-web3-openapi-cli get:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/social/hype/rank/leaderboard \
chainId=56 targetLanguage=en timeRange=1 sentiment=All socialLanguage=ALL
# Unified token rank
binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/unified/rank/list \
'{"rankType":10,"chainId":"56","period":50,"page":1,"size":20}'
# Meme rush rank
binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/pulse/rank/list \
'{"chainId":"CT_501","rankType":10,"limit":20}'
# Smart money signals
binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/buw/wallet/web/signal/smart-money \
'{"smartSignalType":"","page":1,"pageSize":50,"chainId":"56"}'
```
## Token Audit Example
```bash
request_id="$(uuidgen)"
binance-web3-openapi-cli post:/bapi/defi/v1/public/wallet-direct/security/token/audit \
"{\"binanceChainId\":\"56\",\"contractAddress\":\"0x55d398326f99059ff775485246999027b3197955\",\"requestId\":\"request_id\"}"
```
## Fallback Equivalence
- `binance-web3-openapi-cli <operation> ...` is equivalent to
`uxc https://web3.binance.com --schema-url <binance_web3_openapi_schema> <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/binance-web3-openapi-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
SCHEMA_FILE="SKILL_DIR/references/binance-web3.openapi.json"
USAGE_FILE="SKILL_DIR/references/usage-patterns.md"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
need_cmd jq
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"USAGE_FILE"
"SCHEMA_FILE"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
rg -q '^name:\s*binance-web3-openapi-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v binance-web3-openapi-cli' "SKILL_FILE" || fail 'missing link-first command check'
rg -q 'uxc link binance-web3-openapi-cli https://web3.binance.com --schema-url ' "SKILL_FILE" || fail 'missing fixed link create command with schema-url'
rg -q 'binance-web3-openapi-cli -h' "SKILL_FILE" || fail 'missing help-first host discovery example'
rg -q 'binance-web3-openapi-cli get:/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q 'UUID v4 `requestId`' "SKILL_FILE" || fail 'missing audit requestId guardrail'
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "USAGE_FILE"; then
fail 'found banned legacy JSON argument pattern'
fi
rg -q '^\s*display_name:\s*"Binance Web3 API"\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$binance-web3-openapi-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $binance-web3-openapi-skill'
jq -e '.openapi == "3.1.0"' "SCHEMA_FILE" >/dev/null || fail 'schema must be OpenAPI 3.1.0 JSON'
jq -e '.paths["/bapi/defi/v5/public/wallet-direct/buw/wallet/market/token/search"].get' "SCHEMA_FILE" >/dev/null || fail 'missing token search path'
jq -e '.paths["/bapi/defi/v1/public/wallet-direct/security/token/audit"].post' "SCHEMA_FILE" >/dev/null || fail 'missing token audit path'
echo "skills/binance-web3-openapi-skill validation passed"
Operate Binance Spot market, account, and order APIs through UXC with a curated OpenAPI schema, Binance query signing, and separate mainnet/testnet link flows.
---
name: binance-spot-openapi-skill
description: Operate Binance Spot market, account, and order APIs through UXC with a curated OpenAPI schema, Binance query signing, and separate mainnet/testnet link flows.
---
# Binance Spot API Skill
Use this skill to run Binance Spot REST operations through `uxc` + OpenAPI.
Reuse the `uxc` skill for shared execution, auth, and error-handling guidance.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to:
- `https://api.binance.com`
- `https://testnet.binance.vision`
- Access to the curated OpenAPI schema URL:
- `https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-spot-openapi-skill/references/binance-spot.openapi.json`
## Scope
This skill covers curated Binance Spot REST endpoints for:
- public market reads
- signed account reads
- signed order queries
- test orders
- order placement and cancellation
This skill does **not** cover:
- OCO, OTO, OTOCO, OPO, OPOCO, or other `orderList/*` endpoints
- `order/cancelReplace`
- `order/amend/*`
- `historicalTrades`
- `uiKlines`
- `ticker/tradingDay`
- SOR endpoints
- Wallet, Margin, Earn, or `/sapi/*` endpoints
- RSA request signing
- `https://demo-api.binance.com`
## Authentication
Public market endpoints do not require credentials.
Signed Spot endpoints require:
- `api_key` field for `X-MBX-APIKEY`
- `private_key` field for Ed25519 PKCS#8 PEM signing, or `secret_key` for deprecated HMAC SHA256 signing
### Testnet API Key Setup
Binance Spot testnet uses a separate host and separate API key records from mainnet:
- base URL: `https://testnet.binance.vision`
- testnet API keys do not work on mainnet
- mainnet API keys do not work on testnet
There are two practical testnet flows:
1. `Ed25519` (recommended by Binance)
- Generate an Ed25519 keypair locally.
- Register the public key in the Spot testnet API management UI.
- After registration, Binance shows a distinct `API key` for that Ed25519 key record.
- Use that displayed `API key` in `X-MBX-APIKEY`, and use the matching private key PEM for signing.
2. `HMAC` (legacy compatibility)
- Create an HMAC key in the Spot testnet API management UI.
- Binance shows both `API key` and `Secret key`.
- Use `API key` in `X-MBX-APIKEY`, and use `Secret key` for HMAC SHA256 signing.
Important:
- The Ed25519 public key you upload is **not** the `API key`.
- Each Binance key record has its own `API key`.
- Do not mix an HMAC `API key` with an Ed25519 private key, or an Ed25519 `API key` with an HMAC secret.
- If you do, Binance returns `-1022 Signature for this request is not valid.`
### Recommended Credential Setup
Binance recommends `Ed25519`. Store the private key PEM text in an environment variable, or source it from 1Password.
```bash
export BINANCE_TESTNET_ED25519_PRIVATE_KEY="$(cat /absolute/path/to/binance_testnet_ed25519_private.pem)"
export BINANCE_MAINNET_ED25519_PRIVATE_KEY="$(cat /absolute/path/to/binance_mainnet_ed25519_private.pem)"
```
Create one credential per environment so mainnet and testnet keys are never mixed:
```bash
uxc auth credential set binance-spot-mainnet \
--auth-type api_key \
--field api_key=env:BINANCE_MAINNET_API_KEY \
--field private_key=env:BINANCE_MAINNET_ED25519_PRIVATE_KEY
uxc auth credential set binance-spot-testnet \
--auth-type api_key \
--field api_key=env:BINANCE_TESTNET_API_KEY \
--field private_key=env:BINANCE_TESTNET_ED25519_PRIVATE_KEY
```
Add one signer binding per environment:
```bash
uxc auth binding add \
--id binance-spot-mainnet \
--host api.binance.com \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-mainnet \
--signer-json '{"kind":"ed25519_query_v1","algorithm":"ed25519","signing_field":"private_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"base64","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
uxc auth binding add \
--id binance-spot-testnet \
--host testnet.binance.vision \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-testnet \
--signer-json '{"kind":"ed25519_query_v1","algorithm":"ed25519","signing_field":"private_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"base64","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
```
### HMAC Fallback
If you already have legacy HMAC keys, `uxc` still supports them:
```bash
uxc auth credential set binance-spot-mainnet-hmac \
--auth-type api_key \
--field api_key=env:BINANCE_MAINNET_API_KEY \
--field secret_key=env:BINANCE_MAINNET_SECRET_KEY
uxc auth credential set binance-spot-testnet-hmac \
--auth-type api_key \
--field api_key=env:BINANCE_TESTNET_API_KEY \
--field secret_key=env:BINANCE_TESTNET_SECRET_KEY
uxc auth binding add \
--id binance-spot-mainnet-hmac \
--host api.binance.com \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-mainnet-hmac \
--signer-json '{"kind":"hmac_query_v1","algorithm":"hmac_sha256","signing_field":"secret_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"hex","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
uxc auth binding add \
--id binance-spot-testnet-hmac \
--host testnet.binance.vision \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-testnet-hmac \
--signer-json '{"kind":"hmac_query_v1","algorithm":"hmac_sha256","signing_field":"secret_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"hex","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
```
## Core Workflow
1. Use fixed link commands by default:
- `command -v binance-spot-mainnet-openapi-cli`
- If missing, create it:
`uxc link binance-spot-mainnet-openapi-cli https://api.binance.com --schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-spot-openapi-skill/references/binance-spot.openapi.json`
- `command -v binance-spot-testnet-openapi-cli`
- If missing, create it:
`uxc link binance-spot-testnet-openapi-cli https://testnet.binance.vision --schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-spot-openapi-skill/references/binance-spot.openapi.json`
2. Discover operations with help-first flow:
- `binance-spot-mainnet-openapi-cli -h`
- `binance-spot-testnet-openapi-cli -h`
- `binance-spot-testnet-openapi-cli post:/api/v3/order/test -h`
- `binance-spot-testnet-openapi-cli get:/api/v3/account -h`
3. Execute reads first:
- public read:
`binance-spot-mainnet-openapi-cli get:/api/v3/ticker/price symbol=BTCUSDT`
- signed read:
`binance-spot-testnet-openapi-cli get:/api/v3/account omitZeroBalances=true recvWindow=5000`
4. Use `order/test` before real writes:
- `binance-spot-testnet-openapi-cli post:/api/v3/order/test symbol=BTCUSDT side=BUY type=MARKET quoteOrderQty=100 recvWindow=5000`
## Operation Groups
### Public Market
- `get:/api/v3/ping`
- `get:/api/v3/time`
- `get:/api/v3/exchangeInfo`
- `get:/api/v3/avgPrice`
- `get:/api/v3/depth`
- `get:/api/v3/klines`
- `get:/api/v3/ticker/24hr`
- `get:/api/v3/ticker/price`
- `get:/api/v3/trades`
### Signed Reads
- `get:/api/v3/account`
- `get:/api/v3/openOrders`
- `get:/api/v3/order`
- `get:/api/v3/allOrders`
- `get:/api/v3/myTrades`
- `get:/api/v3/rateLimit/order`
### Signed Writes
- `post:/api/v3/order/test`
- `post:/api/v3/order`
- `delete:/api/v3/order`
- `delete:/api/v3/openOrders`
## Guardrails
- Keep automation on the JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Treat `order/test` as the default validation path before any real order placement.
- Treat all mainnet write operations as high-risk and require explicit user confirmation before execution.
- Prefer testnet for all signed examples unless the user explicitly asks for mainnet.
- Before placing orders, query `exchangeInfo` for symbol filters and `ticker/price` or `depth` for current market context.
- If Binance returns `-1021`, call `get:/api/v3/time`, then retry with a fresh timestamp and, if needed, a larger `recvWindow`.
- If Binance returns `-1022`, first verify you are using the `API key` from the same Binance key record as the signing material:
- Ed25519: displayed `API key` + matching private key PEM
- HMAC: displayed `API key` + matching `Secret key`
- Use `--path-prefix /api/v3` on auth bindings. `uxc` now resolves OpenAPI auth against the final operation URL, so this narrower binding works for signed Spot requests and avoids over-broad host-level matching.
- `timestamp` and `signature` are injected by the signer binding; users normally provide business parameters plus optional `recvWindow`.
- `binance-spot-*-openapi-cli <operation> ...` is equivalent to `uxc <host> --schema-url <binance_spot_openapi_schema> <operation> ...`.
## References
- Usage patterns: `references/usage-patterns.md`
- Curated OpenAPI schema: `references/binance-spot.openapi.json`
- Binance Spot skill source material: https://github.com/binance/binance-skills-hub/tree/main/skills/binance/spot
- Official Binance Spot API docs: https://github.com/binance/binance-spot-api-docs
FILE:agents/openai.yaml
interface:
display_name: "Binance Spot API"
short_description: "Operate Binance Spot public market, signed account, and order endpoints via UXC with curated OpenAPI mapping and Ed25519 (recommended) and HMAC query signing"
default_prompt: "Use $binance-spot-openapi-skill to discover and execute Binance Spot REST operations through UXC with separate mainnet/testnet link flows, Ed25519 (recommended) and HMAC signer bindings, and testnet-first write guardrails."
FILE:references/binance-spot.openapi.json
{
"openapi": "3.1.0",
"info": {
"title": "Binance Spot API",
"version": "1.0.0",
"description": "Curated OpenAPI schema for high-value Binance Spot market, account, and order endpoints."
},
"servers": [
{
"url": "https://api.binance.com"
},
{
"url": "https://testnet.binance.vision"
}
],
"paths": {
"/api/v3/ping": {
"get": {
"operationId": "ping",
"summary": "Test connectivity",
"responses": {
"200": {
"description": "Connectivity check response",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
}
},
"/api/v3/time": {
"get": {
"operationId": "getServerTime",
"summary": "Check server time",
"responses": {
"200": {
"description": "Server time",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"serverTime": {
"type": "integer"
}
},
"required": [
"serverTime"
],
"additionalProperties": true
}
}
}
}
}
}
},
"/api/v3/exchangeInfo": {
"get": {
"operationId": "getExchangeInfo",
"summary": "Exchange information",
"parameters": [
{
"name": "symbol",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "symbols",
"in": "query",
"schema": {
"type": "string",
"description": "JSON-style symbols array accepted by Binance."
}
},
{
"name": "permissions",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "showPermissionSets",
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "symbolStatus",
"in": "query",
"schema": {
"$ref": "#/components/schemas/SymbolStatus"
}
}
],
"responses": {
"200": {
"description": "Exchange information",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ExchangeInfo"
}
}
}
}
}
}
},
"/api/v3/avgPrice": {
"get": {
"operationId": "getAveragePrice",
"summary": "Current average price",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Average price",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"mins": {
"type": "integer"
},
"price": {
"type": "string"
},
"closeTime": {
"type": "integer"
}
},
"additionalProperties": true
}
}
}
}
}
}
},
"/api/v3/depth": {
"get": {
"operationId": "getOrderBook",
"summary": "Order book",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 100
}
},
{
"name": "symbolStatus",
"in": "query",
"schema": {
"$ref": "#/components/schemas/SymbolStatus"
}
}
],
"responses": {
"200": {
"description": "Order book",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"lastUpdateId": {
"type": "integer"
},
"bids": {
"$ref": "#/components/schemas/PriceLevelArray"
},
"asks": {
"$ref": "#/components/schemas/PriceLevelArray"
}
},
"additionalProperties": true
}
}
}
}
}
}
},
"/api/v3/klines": {
"get": {
"operationId": "getKlines",
"summary": "Kline candlestick data",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "interval",
"in": "query",
"required": true,
"schema": {
"$ref": "#/components/schemas/KlineInterval"
}
},
{
"name": "startTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "endTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "timeZone",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 500
}
}
],
"responses": {
"200": {
"description": "Kline rows",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/KlineRow"
}
}
}
}
}
}
}
},
"/api/v3/ticker/24hr": {
"get": {
"operationId": "getTicker24hr",
"summary": "24hr ticker price change statistics",
"parameters": [
{
"name": "symbol",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "symbols",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "type",
"in": "query",
"schema": {
"$ref": "#/components/schemas/TickerType"
}
},
{
"name": "symbolStatus",
"in": "query",
"schema": {
"$ref": "#/components/schemas/SymbolStatus"
}
}
],
"responses": {
"200": {
"description": "24hr ticker stats",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/Ticker24hr"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/Ticker24hr"
}
}
]
}
}
}
}
}
}
},
"/api/v3/ticker/price": {
"get": {
"operationId": "getTickerPrice",
"summary": "Symbol price ticker",
"parameters": [
{
"name": "symbol",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "symbols",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "symbolStatus",
"in": "query",
"schema": {
"$ref": "#/components/schemas/SymbolStatus"
}
}
],
"responses": {
"200": {
"description": "Ticker price result",
"content": {
"application/json": {
"schema": {
"oneOf": [
{
"$ref": "#/components/schemas/TickerPrice"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/TickerPrice"
}
}
]
}
}
}
}
}
}
},
"/api/v3/trades": {
"get": {
"operationId": "getRecentTrades",
"summary": "Recent trades list",
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 500
}
}
],
"responses": {
"200": {
"description": "Recent trades",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RecentTrade"
}
}
}
}
}
}
}
},
"/api/v3/account": {
"get": {
"operationId": "getAccount",
"summary": "Account information",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "omitZeroBalances",
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Account information",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AccountInfo"
}
}
}
}
}
}
},
"/api/v3/openOrders": {
"get": {
"operationId": "getOpenOrders",
"summary": "Current open orders",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Open orders",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
}
},
"delete": {
"operationId": "cancelOpenOrders",
"summary": "Cancel all open orders on a symbol",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Cancel all open orders result",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
}
}
},
"/api/v3/order/test": {
"post": {
"operationId": "testOrder",
"summary": "Test new order",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/OrderSymbol"
},
{
"$ref": "#/components/parameters/OrderSide"
},
{
"$ref": "#/components/parameters/OrderType"
},
{
"$ref": "#/components/parameters/TimeInForce"
},
{
"$ref": "#/components/parameters/Quantity"
},
{
"$ref": "#/components/parameters/QuoteOrderQty"
},
{
"$ref": "#/components/parameters/Price"
},
{
"$ref": "#/components/parameters/NewClientOrderId"
},
{
"$ref": "#/components/parameters/RecvWindow"
},
{
"name": "newOrderRespType",
"in": "query",
"schema": {
"$ref": "#/components/schemas/NewOrderRespType"
}
}
],
"responses": {
"200": {
"description": "Test order result",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
}
},
"/api/v3/order": {
"get": {
"operationId": "getOrder",
"summary": "Query order",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "orderId",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "origClientOrderId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Order status",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
},
"post": {
"operationId": "createOrder",
"summary": "New order",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"$ref": "#/components/parameters/OrderSymbol"
},
{
"$ref": "#/components/parameters/OrderSide"
},
{
"$ref": "#/components/parameters/OrderType"
},
{
"$ref": "#/components/parameters/TimeInForce"
},
{
"$ref": "#/components/parameters/Quantity"
},
{
"$ref": "#/components/parameters/QuoteOrderQty"
},
{
"$ref": "#/components/parameters/Price"
},
{
"$ref": "#/components/parameters/NewClientOrderId"
},
{
"$ref": "#/components/parameters/RecvWindow"
},
{
"name": "newOrderRespType",
"in": "query",
"schema": {
"$ref": "#/components/schemas/NewOrderRespType"
}
}
],
"responses": {
"200": {
"description": "Order placement result",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
},
"delete": {
"operationId": "cancelOrder",
"summary": "Cancel order",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "orderId",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "origClientOrderId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "newClientOrderId",
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "cancelRestrictions",
"in": "query",
"schema": {
"$ref": "#/components/schemas/CancelRestrictions"
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Cancel order result",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
}
},
"/api/v3/allOrders": {
"get": {
"operationId": "getAllOrders",
"summary": "All orders",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "orderId",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "startTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "endTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 500
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "All orders",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Order"
}
}
}
}
}
}
}
},
"/api/v3/myTrades": {
"get": {
"operationId": "getMyTrades",
"summary": "Account trade list",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "orderId",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "startTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "endTime",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "fromId",
"in": "query",
"schema": {
"type": "integer"
}
},
{
"name": "limit",
"in": "query",
"schema": {
"type": "integer",
"default": 500
}
},
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Account trades",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Trade"
}
}
}
}
}
}
}
},
"/api/v3/rateLimit/order": {
"get": {
"operationId": "getOrderRateLimit",
"summary": "Query current order count usage",
"security": [
{
"api_key": []
}
],
"parameters": [
{
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
],
"responses": {
"200": {
"description": "Order count usage",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"rateLimitType": {
"type": "string"
},
"interval": {
"type": "string"
},
"intervalNum": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"count": {
"type": "integer"
}
},
"additionalProperties": true
}
}
}
}
}
}
}
}
},
"components": {
"securitySchemes": {
"api_key": {
"type": "apiKey",
"in": "header",
"name": "X-MBX-APIKEY"
}
},
"parameters": {
"OrderSymbol": {
"name": "symbol",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
"OrderSide": {
"name": "side",
"in": "query",
"required": true,
"schema": {
"$ref": "#/components/schemas/OrderSide"
}
},
"OrderType": {
"name": "type",
"in": "query",
"required": true,
"schema": {
"$ref": "#/components/schemas/OrderType"
}
},
"TimeInForce": {
"name": "timeInForce",
"in": "query",
"schema": {
"$ref": "#/components/schemas/TimeInForce"
}
},
"Quantity": {
"name": "quantity",
"in": "query",
"schema": {
"type": "string"
}
},
"QuoteOrderQty": {
"name": "quoteOrderQty",
"in": "query",
"schema": {
"type": "string"
}
},
"Price": {
"name": "price",
"in": "query",
"schema": {
"type": "string"
}
},
"NewClientOrderId": {
"name": "newClientOrderId",
"in": "query",
"schema": {
"type": "string"
}
},
"RecvWindow": {
"name": "recvWindow",
"in": "query",
"schema": {
"$ref": "#/components/schemas/RecvWindow"
}
}
},
"schemas": {
"RecvWindow": {
"type": "number",
"description": "Binance recvWindow in milliseconds, up to 60000 and may contain decimal precision."
},
"KlineInterval": {
"type": "string",
"enum": [
"1s",
"1m",
"3m",
"5m",
"15m",
"30m",
"1h",
"2h",
"4h",
"6h",
"8h",
"12h",
"1d",
"3d",
"1w",
"1M"
]
},
"TickerType": {
"type": "string",
"enum": [
"FULL",
"MINI"
]
},
"SymbolStatus": {
"type": "string",
"enum": [
"TRADING",
"END_OF_DAY",
"HALT",
"BREAK"
]
},
"OrderSide": {
"type": "string",
"enum": [
"BUY",
"SELL"
]
},
"OrderType": {
"type": "string",
"enum": [
"LIMIT",
"MARKET",
"STOP_LOSS",
"STOP_LOSS_LIMIT",
"TAKE_PROFIT",
"TAKE_PROFIT_LIMIT",
"LIMIT_MAKER"
]
},
"TimeInForce": {
"type": "string",
"enum": [
"GTC",
"IOC",
"FOK"
]
},
"NewOrderRespType": {
"type": "string",
"enum": [
"ACK",
"RESULT",
"FULL"
]
},
"CancelRestrictions": {
"type": "string",
"enum": [
"ONLY_NEW",
"ONLY_PARTIALLY_FILLED"
]
},
"PriceLevelArray": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"KlineRow": {
"type": "array",
"prefixItems": [
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "string"
},
{
"type": "string"
},
{
"type": "string"
}
],
"items": false,
"minItems": 12,
"maxItems": 12
},
"TickerPrice": {
"type": "object",
"properties": {
"symbol": {
"type": "string"
},
"price": {
"type": "string"
}
},
"required": [
"symbol",
"price"
],
"additionalProperties": true
},
"Ticker24hr": {
"type": "object",
"properties": {
"symbol": {
"type": "string"
},
"priceChange": {
"type": "string"
},
"priceChangePercent": {
"type": "string"
},
"weightedAvgPrice": {
"type": "string"
},
"lastPrice": {
"type": "string"
},
"volume": {
"type": "string"
},
"quoteVolume": {
"type": "string"
},
"openTime": {
"type": "integer"
},
"closeTime": {
"type": "integer"
}
},
"required": [
"symbol"
],
"additionalProperties": true
},
"RecentTrade": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"price": {
"type": "string"
},
"qty": {
"type": "string"
},
"quoteQty": {
"type": "string"
},
"time": {
"type": "integer"
},
"isBuyerMaker": {
"type": "boolean"
},
"isBestMatch": {
"type": "boolean"
}
},
"additionalProperties": true
},
"Order": {
"type": "object",
"properties": {
"symbol": {
"type": "string"
},
"orderId": {
"type": "integer"
},
"orderListId": {
"type": "integer"
},
"clientOrderId": {
"type": "string"
},
"price": {
"type": "string"
},
"origQty": {
"type": "string"
},
"executedQty": {
"type": "string"
},
"cummulativeQuoteQty": {
"type": "string"
},
"status": {
"type": "string"
},
"timeInForce": {
"type": "string"
},
"type": {
"type": "string"
},
"side": {
"type": "string"
},
"stopPrice": {
"type": "string"
},
"icebergQty": {
"type": "string"
},
"time": {
"type": "integer"
},
"updateTime": {
"type": "integer"
},
"isWorking": {
"type": "boolean"
},
"workingTime": {
"type": "integer"
},
"origQuoteOrderQty": {
"type": "string"
}
},
"required": [
"symbol",
"orderId",
"status",
"type",
"side"
],
"additionalProperties": true
},
"Trade": {
"type": "object",
"properties": {
"symbol": {
"type": "string"
},
"id": {
"type": "integer"
},
"orderId": {
"type": "integer"
},
"orderListId": {
"type": "integer"
},
"price": {
"type": "string"
},
"qty": {
"type": "string"
},
"quoteQty": {
"type": "string"
},
"commission": {
"type": "string"
},
"commissionAsset": {
"type": "string"
},
"time": {
"type": "integer"
},
"isBuyer": {
"type": "boolean"
},
"isMaker": {
"type": "boolean"
},
"isBestMatch": {
"type": "boolean"
}
},
"additionalProperties": true
},
"Balance": {
"type": "object",
"properties": {
"asset": {
"type": "string"
},
"free": {
"type": "string"
},
"locked": {
"type": "string"
}
},
"required": [
"asset",
"free",
"locked"
]
},
"AccountInfo": {
"type": "object",
"properties": {
"makerCommission": {
"type": "integer"
},
"takerCommission": {
"type": "integer"
},
"buyerCommission": {
"type": "integer"
},
"sellerCommission": {
"type": "integer"
},
"canTrade": {
"type": "boolean"
},
"canWithdraw": {
"type": "boolean"
},
"canDeposit": {
"type": "boolean"
},
"brokered": {
"type": "boolean"
},
"requireSelfTradePrevention": {
"type": "boolean"
},
"preventSor": {
"type": "boolean"
},
"updateTime": {
"type": "integer"
},
"accountType": {
"type": "string"
},
"balances": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Balance"
}
},
"permissions": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"balances"
],
"additionalProperties": true
},
"ExchangeInfo": {
"type": "object",
"properties": {
"timezone": {
"type": "string"
},
"serverTime": {
"type": "integer"
},
"rateLimits": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
},
"exchangeFilters": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
},
"symbols": {
"type": "array",
"items": {
"type": "object",
"properties": {
"symbol": {
"type": "string"
},
"status": {
"type": "string"
},
"baseAsset": {
"type": "string"
},
"quoteAsset": {
"type": "string"
},
"filters": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true
}
}
},
"required": [
"symbol",
"status"
],
"additionalProperties": true
}
}
},
"additionalProperties": true
}
}
}
}
FILE:references/usage-patterns.md
# Binance Spot API Skill - Usage Patterns
## Link Setup
```bash
command -v binance-spot-mainnet-openapi-cli
uxc link binance-spot-mainnet-openapi-cli https://api.binance.com \
--schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-spot-openapi-skill/references/binance-spot.openapi.json
command -v binance-spot-testnet-openapi-cli
uxc link binance-spot-testnet-openapi-cli https://testnet.binance.vision \
--schema-url https://raw.githubusercontent.com/holon-run/uxc/main/skills/binance-spot-openapi-skill/references/binance-spot.openapi.json
binance-spot-mainnet-openapi-cli -h
binance-spot-testnet-openapi-cli -h
```
## Auth Setup
### Get Spot Testnet Keys
- Open `https://testnet.binance.vision`
- Create or select a Spot API key record in the testnet UI
- For `Ed25519`, generate the keypair locally first, upload the public key, then copy the `API key` Binance shows for that record
- For `HMAC`, Binance shows both `API key` and `Secret key`
- Keep the `API key` matched with the same record as the signing material
If you want to generate an Ed25519 keypair locally:
```bash
openssl genpkey -algorithm ed25519 -out binance_testnet_ed25519_private.pem
openssl pkey -in binance_testnet_ed25519_private.pem -pubout -out binance_testnet_ed25519_public.pem
```
Then export the private key PEM content and the Binance-displayed API key:
```bash
export BINANCE_TESTNET_API_KEY='<binance testnet api key for this Ed25519 record>'
export BINANCE_TESTNET_ED25519_PRIVATE_KEY="$(cat /absolute/path/to/binance_testnet_ed25519_private.pem)"
```
### Ed25519 Setup
```bash
uxc auth credential set binance-spot-mainnet \
--auth-type api_key \
--field api_key=env:BINANCE_MAINNET_API_KEY \
--field private_key=env:BINANCE_MAINNET_ED25519_PRIVATE_KEY
uxc auth credential set binance-spot-testnet \
--auth-type api_key \
--field api_key=env:BINANCE_TESTNET_API_KEY \
--field private_key=env:BINANCE_TESTNET_ED25519_PRIVATE_KEY
```
```bash
SIGNER='{"kind":"ed25519_query_v1","algorithm":"ed25519","signing_field":"private_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"base64","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}'
uxc auth binding add \
--id binance-spot-mainnet \
--host api.binance.com \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-mainnet \
--signer-json "$SIGNER" \
--priority 100
uxc auth binding add \
--id binance-spot-testnet \
--host testnet.binance.vision \
--path-prefix /api/v3 \
--scheme https \
--credential binance-spot-testnet \
--signer-json "$SIGNER" \
--priority 100
```
## Legacy HMAC Setup
```bash
uxc auth credential set binance-spot-mainnet-hmac \
--auth-type api_key \
--field api_key=env:BINANCE_MAINNET_API_KEY \
--field secret_key=env:BINANCE_MAINNET_SECRET_KEY
uxc auth credential set binance-spot-testnet-hmac \
--auth-type api_key \
--field api_key=env:BINANCE_TESTNET_API_KEY \
--field secret_key=env:BINANCE_TESTNET_SECRET_KEY
HMAC_SIGNER='{"kind":"hmac_query_v1","algorithm":"hmac_sha256","signing_field":"secret_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"hex","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}'
```
Common failure mode:
- `-1022 Signature for this request is not valid.`
- Usually means the `API key` and signing material came from different Binance key records.
- Ed25519 requires the Binance-displayed `API key` for that uploaded public key record.
- The uploaded public key itself is not the `API key`.
## Public Read Examples
```bash
# Server time
binance-spot-mainnet-openapi-cli get:/api/v3/time
# Exchange filters and symbol metadata
binance-spot-mainnet-openapi-cli get:/api/v3/exchangeInfo symbol=BTCUSDT
# Latest ticker price
binance-spot-mainnet-openapi-cli get:/api/v3/ticker/price symbol=BTCUSDT
# 24h stats
binance-spot-mainnet-openapi-cli get:/api/v3/ticker/24hr symbol=BTCUSDT
# Recent trades
binance-spot-mainnet-openapi-cli get:/api/v3/trades symbol=BTCUSDT limit=20
# Klines
binance-spot-mainnet-openapi-cli get:/api/v3/klines symbol=BTCUSDT interval=1h limit=24
# Order book
binance-spot-mainnet-openapi-cli get:/api/v3/depth symbol=BTCUSDT limit=20
```
## Signed Read Examples
```bash
# Account balances on testnet
binance-spot-testnet-openapi-cli get:/api/v3/account omitZeroBalances=true recvWindow=5000
# Current open orders
binance-spot-testnet-openapi-cli get:/api/v3/openOrders symbol=BTCUSDT recvWindow=5000
# Query one order
binance-spot-testnet-openapi-cli get:/api/v3/order symbol=BTCUSDT orderId=123456 recvWindow=5000
# Order history
binance-spot-testnet-openapi-cli get:/api/v3/allOrders symbol=BTCUSDT limit=20 recvWindow=5000
# Trade history
binance-spot-testnet-openapi-cli get:/api/v3/myTrades symbol=BTCUSDT limit=20 recvWindow=5000
# Current order count usage
binance-spot-testnet-openapi-cli get:/api/v3/rateLimit/order recvWindow=5000
```
## Testnet-First Write Examples
```bash
# Validate order shape and signature without placing a real order
binance-spot-testnet-openapi-cli post:/api/v3/order/test \
symbol=BTCUSDT side=BUY type=MARKET quoteOrderQty=100 recvWindow=5000
# Place a real testnet order
binance-spot-testnet-openapi-cli post:/api/v3/order \
symbol=BTCUSDT side=BUY type=LIMIT timeInForce=GTC quantity=0.001 price=20000 recvWindow=5000
# Cancel a testnet order
binance-spot-testnet-openapi-cli delete:/api/v3/order \
symbol=BTCUSDT orderId=123456 recvWindow=5000
# Cancel all open orders on a symbol
binance-spot-testnet-openapi-cli delete:/api/v3/openOrders \
symbol=BTCUSDT recvWindow=5000
```
## Mainnet Write Guardrail
```bash
# Only run after explicit user confirmation
binance-spot-mainnet-openapi-cli post:/api/v3/order \
symbol=BTCUSDT side=BUY type=MARKET quoteOrderQty=100 recvWindow=5000
```
## Fallback Equivalence
- `binance-spot-mainnet-openapi-cli <operation> ...` is equivalent to
`uxc https://api.binance.com --schema-url <binance_spot_openapi_schema> <operation> ...`.
- `binance-spot-testnet-openapi-cli <operation> ...` is equivalent to
`uxc https://testnet.binance.vision --schema-url <binance_spot_openapi_schema> <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/binance-spot-openapi-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
SCHEMA_FILE="SKILL_DIR/references/binance-spot.openapi.json"
USAGE_FILE="SKILL_DIR/references/usage-patterns.md"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
need_cmd jq
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"USAGE_FILE"
"SCHEMA_FILE"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
rg -q '^name:\s*binance-spot-openapi-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v binance-spot-mainnet-openapi-cli' "SKILL_FILE" || fail 'missing mainnet link-first check'
rg -q 'command -v binance-spot-testnet-openapi-cli' "SKILL_FILE" || fail 'missing testnet link-first check'
rg -q 'uxc link binance-spot-mainnet-openapi-cli https://api.binance.com --schema-url ' "SKILL_FILE" || fail 'missing fixed mainnet link command'
rg -q 'uxc link binance-spot-testnet-openapi-cli https://testnet.binance.vision --schema-url ' "SKILL_FILE" || fail 'missing fixed testnet link command'
rg -q 'post:/api/v3/order/test -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q 'Treat all mainnet write operations as high-risk' "SKILL_FILE" || fail 'missing mainnet write guardrail'
rg -q 'timestamp` and `signature` are injected by the signer binding' "SKILL_FILE" || fail 'missing signer injection note'
rg -q -- '--field api_key=env:BINANCE_MAINNET_API_KEY' "SKILL_FILE" || fail 'missing mainnet auth example'
rg -q -- '--signer-json' "SKILL_FILE" || fail 'missing signer-json example'
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "USAGE_FILE"; then
fail 'found banned legacy JSON argument pattern'
fi
rg -q '^\s*display_name:\s*"Binance Spot API"\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$binance-spot-openapi-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $binance-spot-openapi-skill'
jq -e '.openapi == "3.1.0"' "SCHEMA_FILE" >/dev/null || fail 'schema must be OpenAPI 3.1.0 JSON'
jq -e '.paths["/api/v3/order"].post.operationId == "createOrder"' "SCHEMA_FILE" >/dev/null || fail 'missing create order operation'
jq -e '.paths["/api/v3/account"].get.operationId == "getAccount"' "SCHEMA_FILE" >/dev/null || fail 'missing account operation'
jq -e '.paths["/api/v3/order/test"].post.operationId == "testOrder"' "SCHEMA_FILE" >/dev/null || fail 'missing test order operation'
echo "skills/binance-spot-openapi-skill validation passed"
Use Bitquery GraphQL through UXC for onchain trades, transfers, token holder analysis, balances, and market structure queries across supported networks, with...
---
name: bitquery-graphql-skill
description: Use Bitquery GraphQL through UXC for onchain trades, transfers, token holder analysis, balances, and market structure queries across supported networks, with OAuth client_credentials authentication and query-first execution.
---
# Bitquery GraphQL Skill
Use this skill to run Bitquery GraphQL API operations through `uxc`.
Reuse the `uxc` skill for discovery, GraphQL execution, OAuth lifecycle, and generic error handling.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://streaming.bitquery.io/graphql`.
- A Bitquery application `client_id` and `client_secret` are available.
## Authentication
Bitquery uses bearer access tokens. The most stable agent path is OAuth `client_credentials`, not a copied temporary token.
1. Create a Bitquery application and note:
- application `client_id`
- application `client_secret`
- token scope `api`
2. Login once with OAuth client credentials:
- `uxc auth oauth login bitquery-graphql --endpoint https://streaming.bitquery.io/graphql --flow client_credentials --client-id <client_id> --client-secret <client_secret> --scope api --token-endpoint https://oauth2.bitquery.io/oauth2/token`
- This flow completes in one command. No browser approval page is required.
3. Bind the GraphQL endpoint:
- `uxc auth binding add --id bitquery-graphql --host streaming.bitquery.io --path-prefix /graphql --scheme https --credential bitquery-graphql --priority 100`
4. Verify auth mapping:
- `uxc auth binding match https://streaming.bitquery.io/graphql`
- `uxc auth oauth info bitquery-graphql`
## Core Workflow
1. Use fixed link command by default:
- `command -v bitquery-graphql-cli`
- If missing, create it: `uxc link bitquery-graphql-cli https://streaming.bitquery.io/graphql`
- `bitquery-graphql-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick another fixed command name.
2. Discover available root operations:
- `bitquery-graphql-cli -h`
- Verified roots currently include `query/EVM`, `query/Solana`, `query/Trading`, `query/Tron`, and matching `subscription/*` roots.
3. Inspect a specific operation:
- `bitquery-graphql-cli query/EVM -h`
- `bitquery-graphql-cli query/Trading -h`
4. Execute with positional JSON and explicit GraphQL selection sets:
- `bitquery-graphql-cli query/EVM '{"network":"base","dataset":"combined","_select":"DEXTrades(limit: {count: 1}) { Transaction { Hash } }"}'`
5. Prefer `query/*` operations first.
- `uxc subscribe` now auto-negotiates modern `graphql-transport-ws` and legacy `graphql-ws` compatibility profiles for `subscription/*`.
- Live Bitquery subscription validation now succeeds when you provide an explicit `_select` that matches a stream-friendly entity shape.
- Prefer `subscription/EVM` as the first validation target rather than `subscription/Trading`.
## Capability Map
- EVM onchain queries:
- `query/EVM`
- `subscription/EVM`
- Solana onchain queries:
- `query/Solana`
- `subscription/Solana`
- Cross-market / trading queries:
- `query/Trading`
- `subscription/Trading`
- Tron onchain queries:
- `query/Tron`
- `subscription/Tron`
Within those roots, Bitquery exposes entities for tasks such as:
- DEX trades
- token balances and holder analysis
- transfers
- blocks and transactions
- mempool and realtime activity
- market or trading views depending on the root
Always inspect the current schema with `-h` and use the narrowest `_select` needed.
For subscriptions specifically:
- always provide `_select`
- start with a high-frequency root such as `subscription/EVM`
- prefer direct event shapes before adding `limit`
- treat empty selections or query-oriented shapes as likely application-level errors
## Recommended Usage Pattern
1. Inspect root arguments first:
- `bitquery-graphql-cli query/EVM -h`
2. Start with a minimal query on one network:
- `bitquery-graphql-cli query/EVM '{"network":"eth","dataset":"combined","_select":"DEXTrades(limit: {count: 1}) { Transaction { Hash } }"}'`
3. Add only the fields needed for the task:
- buyers / sellers
- token addresses
- symbols
- amounts
- timestamps
4. Narrow with GraphQL arguments inside `_select`:
- `limit`
- `orderBy`
- `where`
5. Treat large or realtime queries carefully:
- avoid wide selections
- prefer one chain / token / wallet at a time on first pass
6. For live subscriptions, start with a known-good high-frequency shape:
- `./target/debug/uxc subscribe start https://streaming.bitquery.io/graphql subscription/EVM '{"network":"bsc","mempool":true,"_select":"Transfers { Transaction { Hash From To } Transfer { Amount Type Currency { Name } } }"}' --auth bitquery-graphql --sink file:$HOME/.uxc/subscriptions/bitquery-mempool.ndjson`
## Tested Real Scenario
The following authenticated Bitquery flow was verified successfully through `uxc`:
- OAuth login with `client_credentials`
- auth binding on `https://streaming.bitquery.io/graphql`
- GraphQL host help
- `query/EVM -h`
- authenticated `query/EVM` call on `base`
- daemon-backed `subscription/EVM` over WebSocket against live Bitquery infra
- repeated live `data` events from a BSC mempool transfer stream
The verified query shape was:
```json
{
"network": "base",
"dataset": "combined",
"_select": "DEXTrades(limit: {count: 1}) { Block { Time } Transaction { Hash } Trade { Buy { Amount Buyer Currency { Symbol SmartContract } } Sell { Amount Seller Currency { Symbol SmartContract } } } }"
}
```
The verified subscription shape was:
```json
{
"network": "bsc",
"mempool": true,
"_select": "Transfers { Transaction { Hash From To } Transfer { Amount Type Currency { Name } } }"
}
```
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `bitquery-graphql-cli` as the default command path.
- `bitquery-graphql-cli <operation> ...` is equivalent to `uxc https://streaming.bitquery.io/graphql <operation> ...`.
- Prefer positional JSON for GraphQL calls because `_select` is usually required.
- Keep `_select` small on first pass and add explicit filters before expanding scope.
- Prefer `query/*` for stable agent workflows. `subscription/*` is now validated at runtime, but still depends on provider-specific selection shape.
- For subscription validation or automation, start with `subscription/EVM` and an explicit `_select`; do not assume an empty selection or `subscription/Trading` default shape will yield events.
- If a subscription opens successfully but immediately returns GraphQL errors, treat that as a query-shape problem before assuming transport failure.
- If auth fails:
- confirm `uxc auth binding match https://streaming.bitquery.io/graphql` resolves to `bitquery-graphql`
- inspect token state with `uxc auth oauth info bitquery-graphql`
- manually refresh with `uxc auth oauth refresh bitquery-graphql`
- if needed, rerun `uxc auth oauth login ... --flow client_credentials ...`
- Do not paste temporary IDE tokens into long-lived skill docs. Prefer application-based `client_credentials`.
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Bitquery GraphQL"
short_description: "Trades, transfers, holders, and onchain market queries via Bitquery GraphQL"
default_prompt: "Use $bitquery-graphql-skill to query Bitquery GraphQL for onchain trades, transfers, token holder analysis, balances, and market structure using OAuth client_credentials auth and help-first schema inspection."
FILE:references/usage-patterns.md
# Usage Patterns
This skill defaults to fixed link command `bitquery-graphql-cli`.
## Authentication Setup
Login with OAuth client credentials:
```bash
uxc auth oauth login bitquery-graphql \
--endpoint https://streaming.bitquery.io/graphql \
--flow client_credentials \
--client-id <client_id> \
--client-secret <client_secret> \
--scope api \
--token-endpoint https://oauth2.bitquery.io/oauth2/token
```
Bind the endpoint:
```bash
uxc auth binding add \
--id bitquery-graphql \
--host streaming.bitquery.io \
--path-prefix /graphql \
--scheme https \
--credential bitquery-graphql \
--priority 100
```
Check auth state:
```bash
uxc auth binding match https://streaming.bitquery.io/graphql
uxc auth oauth info bitquery-graphql
```
## Link Setup
```bash
command -v bitquery-graphql-cli
uxc link bitquery-graphql-cli https://streaming.bitquery.io/graphql
bitquery-graphql-cli -h
```
## Help-First Discovery
```bash
bitquery-graphql-cli query/EVM -h
bitquery-graphql-cli query/Trading -h
```
## Query Examples
Minimal EVM trade query:
```bash
bitquery-graphql-cli query/EVM '{"network":"eth","dataset":"combined","_select":"DEXTrades(limit: {count: 1}) { Transaction { Hash } }"}'
```
Verified Base DEX trade query:
```bash
bitquery-graphql-cli query/EVM '{"network":"base","dataset":"combined","_select":"DEXTrades(limit: {count: 1}) { Block { Time } Transaction { Hash } Trade { Buy { Amount Buyer Currency { Symbol SmartContract } } Sell { Amount Seller Currency { Symbol SmartContract } } } }"}'
```
Mempool example:
```bash
bitquery-graphql-cli query/EVM '{"network":"eth","mempool":true,"_select":"DEXTrades(limit: {count: 5}) { Transaction { Hash } }"}'
```
Trading root example:
```bash
bitquery-graphql-cli query/Trading '{"dataset":"combined","_select":"Pairs(limit: {count: 5}) { Market { BaseCurrency { Symbol } QuoteCurrency { Symbol } } }"}'
```
## Subscription Usage
GraphQL subscriptions appear in schema discovery:
```bash
bitquery-graphql-cli subscription/EVM -h
```
Bitquery subscriptions are now validated through `uxc subscribe`, but they still need an explicit `_select` and a stream-friendly shape.
Recommended first live subscription:
```bash
uxc subscribe start https://streaming.bitquery.io/graphql \
subscription/EVM \
'{"network":"bsc","mempool":true,"_select":"Transfers { Transaction { Hash From To } Transfer { Amount Type Currency { Name } } }"}' \
--auth bitquery-graphql \
--sink file:$HOME/.uxc/subscriptions/bitquery-mempool.ndjson
```
Guidance:
- prefer `subscription/EVM` as the first validation target
- always pass `_select`
- do not assume `subscription/Trading` without a selection will stream useful data
- for stable automation, keep `query/*` as the default path unless you explicitly need a live stream
## Output Parsing
Rely on envelope fields:
- Success: `ok == true`, consume `data`
- Failure: `ok == false`, inspect `error.code` and `error.message`
## Fallback Equivalence
- `bitquery-graphql-cli <operation> ...` is equivalent to `uxc https://streaming.bitquery.io/graphql <operation> ...`.
- If link setup is temporarily unavailable, use the direct `uxc "https://streaming.bitquery.io/graphql" ...` form as fallback.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/bitquery-graphql-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
USAGE_FILE="SKILL_DIR/references/usage-patterns.md"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
for file in "SKILL_FILE" "OPENAI_FILE" "USAGE_FILE"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include closing YAML frontmatter"
fi
if ! rg -q '^name:\s*bitquery-graphql-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: bitquery-graphql-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'https://streaming.bitquery.io/graphql' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must document the GraphQL endpoint"
fi
if ! rg -q 'https://oauth2.bitquery.io/oauth2/token' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must document the OAuth token endpoint"
fi
if ! rg -q 'client_credentials' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must document client_credentials auth"
fi
if ! rg -q 'command -v bitquery-graphql-cli' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must include link-first command checks"
fi
if ! rg -q 'uxc link bitquery-graphql-cli https://streaming.bitquery.io/graphql' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must include fixed link creation"
fi
if ! rg -q 'bitquery-graphql-cli -h' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must use help-first discovery"
fi
for op in 'query/EVM' 'query/Trading'; do
if ! rg -q "op" "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must reference op"
fi
done
if ! rg -q '_select' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must explain _select usage"
fi
if ! rg -q "query/EVM '.*_select" "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must include a positional JSON query example"
fi
if ! rg -q 'subscription/\*' "SKILL_FILE"; then
fail "SKILL.md must mention subscription caution"
fi
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://streaming.bitquery.io/graphql' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must include fallback equivalence guidance"
fi
if rg -q -- '--input-json|(^|[[:space:]])bitquery-graphql-cli (list|describe|call)([[:space:]]|$)' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must not use list/describe/call/--input-json in default examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must not pass raw JSON via --args"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "USAGE_FILE"; then
fail "Bitquery docs must not include dynamic command renaming guidance"
fi
if ! rg -q '^\s*display_name:\s*"Bitquery GraphQL"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$bitquery-graphql-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $bitquery-graphql-skill'
fi
echo "skills/bitquery-graphql-skill validation passed"
Use The Graph Token API MCP through UXC for token metadata, wallet balances, transfers, holders, pools, and market data with help-first inspection and Token...
---
name: thegraph-token-mcp-skill
description: Use The Graph Token API MCP through UXC for token metadata, wallet balances, transfers, holders, pools, and market data with help-first inspection and Token API specific JWT bearer auth binding.
---
# The Graph Token MCP Skill
Use this skill to run The Graph Token API MCP operations through `uxc`.
Reuse the `uxc` skill for generic protocol discovery, envelope parsing, and error handling rules.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://token-api.mcp.thegraph.com/`.
- A The Graph Token API `API TOKEN (JWT)` from `https://thegraph.market/dashboard` is available for authenticated calls.
Important auth distinction:
- Do not reuse the `thegraph-mcp-skill` API key directly.
- `thegraph-token-mcp-skill` uses a separate credential sourced from The Graph Market Token API dashboard.
- The value used with `Authorization: Bearer ...` must be the generated `API TOKEN (JWT)`, not the raw dashboard API key.
## Core Workflow
1. Verify endpoint and protocol with help-first probing:
- `uxc https://token-api.mcp.thegraph.com/ -h`
- Confirm protocol is MCP (`protocol == "mcp"` in envelope).
2. Configure a dedicated Token API JWT credential and binding:
- Generate the `API TOKEN (JWT)` in `https://thegraph.market/dashboard`
- Store it separately from the subgraph credential, for example:
- `uxc auth credential set thegraph-token --secret-env THEGRAPH_TOKEN_API_JWT`
- Bind the Token API endpoint to that dedicated credential:
- `uxc auth binding add --id thegraph-token-mcp --host token-api.mcp.thegraph.com --scheme https --credential thegraph-token --priority 100`
3. Use fixed link command by default:
- `command -v thegraph-token-mcp-cli`
- If missing, create it:
- `uxc link thegraph-token-mcp-cli https://token-api.mcp.thegraph.com/`
- `thegraph-token-mcp-cli -h`
4. Inspect operation schema before execution:
- `thegraph-token-mcp-cli getV1Networks -h`
- `thegraph-token-mcp-cli getV1EvmTokens -h`
- `thegraph-token-mcp-cli getV1EvmBalances -h`
5. Prefer read operations first, then narrower wallet/token/pool queries.
## Capability Map
- Service discovery:
- `getV1Health`
- `getV1Version`
- `getV1Networks`
- Token data:
- `getV1EvmTokens`
- `getV1EvmTokensNative`
- Wallet and transfer data:
- `getV1EvmBalances`
- transfer/history operations exposed by the endpoint
- Market and DEX data:
- pool / OHLC / dex operations exposed by the endpoint
- Non-EVM coverage:
- TVM and other chain families exposed by the endpoint
Always inspect host help and operation help in the current endpoint version before relying on an operation name or argument shape.
## Recommended Usage Pattern
1. Start with network discovery:
- `thegraph-token-mcp-cli getV1Networks`
2. Confirm the operation and required arguments with `-h`.
3. Query the narrowest surface first:
- token metadata for one contract
- balances for one address
- one pool / one token / one date range
4. Expand to broader scans only when needed.
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `thegraph-token-mcp-cli` as default command path.
- `thegraph-token-mcp-cli <operation> ...` is equivalent to `uxc https://token-api.mcp.thegraph.com/ <operation> ...` when the same auth binding is configured.
- Use direct `uxc "<endpoint>" ...` only as temporary fallback when link setup is unavailable.
- Prefer `key=value` for simple arguments and positional JSON for nested objects.
- If auth fails:
- confirm `uxc auth credential info thegraph-token` succeeds
- confirm `uxc auth binding match https://token-api.mcp.thegraph.com/` resolves to `thegraph-token`
- confirm the stored secret is the generated `API TOKEN (JWT)`, not the raw API key from The Graph Market dashboard
- rerun `thegraph-token-mcp-cli -h`
## Tested Real Scenario
The endpoint was verified through `uxc` host discovery and returned a live MCP tool list including:
- `getV1Health`
- `getV1Version`
- `getV1Networks`
- `getV1EvmTokens`
- `getV1EvmTokensNative`
- `getV1EvmBalances`
This confirms the skill target is a real MCP surface rather than a direct OpenAPI host.
The Token API requires its own bearer token workflow:
- manage the token in `https://thegraph.market/dashboard`
- generate `API TOKEN (JWT)` from the dashboard-managed Token API key
- store that JWT separately from the subgraph MCP credential
Do not document or implement this skill as if it reused the same raw API key as `thegraph-mcp-skill`.
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "The Graph Token MCP"
short_description: "Token, wallet, transfer, and market data via The Graph Token API MCP"
default_prompt: "Use $thegraph-token-mcp-skill to query The Graph Token API MCP for token metadata, wallet balances, transfers, holders, pools, and market data with help-first inspection and standard auth binding."
FILE:references/usage-patterns.md
# Usage Patterns
All commands in this skill use the native MCP HTTP endpoint:
`https://token-api.mcp.thegraph.com/`
This skill defaults to fixed link command `thegraph-token-mcp-cli`.
Create it when missing:
```bash
command -v thegraph-token-mcp-cli
uxc auth credential set thegraph-token --secret-env THEGRAPH_TOKEN_API_JWT
uxc auth binding add --id thegraph-token-mcp --host token-api.mcp.thegraph.com --scheme https --credential thegraph-token --priority 100
uxc link thegraph-token-mcp-cli https://token-api.mcp.thegraph.com/
thegraph-token-mcp-cli -h
```
Notes:
- Auth is handled through standard `uxc auth credential` + `binding`.
- The Token API credential is separate from `thegraph-mcp-skill`.
- Manage the Token API key in `https://thegraph.market/dashboard`.
- Use the generated `API TOKEN (JWT)` as the bearer secret, not the raw dashboard API key.
- Check the active binding with `uxc auth binding match https://token-api.mcp.thegraph.com/`.
## Discover And Inspect
```bash
thegraph-token-mcp-cli -h
thegraph-token-mcp-cli getV1Networks -h
thegraph-token-mcp-cli getV1EvmTokens -h
thegraph-token-mcp-cli getV1EvmBalances -h
```
## Network Discovery
```bash
thegraph-token-mcp-cli getV1Networks
```
Use this first to confirm supported network identifiers before querying balances, tokens, or pools.
## Token Metadata
```bash
thegraph-token-mcp-cli getV1EvmTokens network=base contract=0x4200000000000000000000000000000000000006
```
Native token metadata:
```bash
thegraph-token-mcp-cli getV1EvmTokensNative network=base
```
## Wallet Balances
```bash
thegraph-token-mcp-cli getV1EvmBalances network=base address=0xYourAddress
```
Use operation help before querying transfers, holders, or market surfaces because exact operation names may evolve.
## Practical Rules
- Start with one network and one address/contract.
- Prefer operation-level help before relying on less common endpoints.
- Use positional JSON for nested inputs or arrays.
- Keep initial queries narrow and inspect response shape before widening scope.
## Fallback Equivalence
- `thegraph-token-mcp-cli <operation> ...` is equivalent to `uxc https://token-api.mcp.thegraph.com/ <operation> ...` when the same auth binding is configured.
- If link setup is temporarily unavailable, use the direct `uxc "<endpoint>" ...` form as fallback.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/thegraph-token-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/scripts/validate.sh"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*thegraph-token-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: thegraph-token-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'token-api\.mcp\.thegraph\.com/' "SKILL_FILE"; then
fail "SKILL.md must document The Graph Token MCP endpoint"
fi
if ! rg -q 'command -v thegraph-token-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link thegraph-token-mcp-cli https://token-api\.mcp\.thegraph\.com/' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include fixed link creation command"
fi
if ! rg -q 'uxc auth credential set thegraph-token --secret-env THEGRAPH_TOKEN_API_JWT' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must configure a dedicated The Graph Token API JWT credential"
fi
if ! rg -q 'uxc auth binding add --id thegraph-token-mcp --host token-api\.mcp\.thegraph\.com --scheme https --credential thegraph-token --priority 100' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include auth binding setup for The Graph Token MCP endpoint"
fi
if ! rg -q 'API TOKEN \(JWT\)' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must explicitly require API TOKEN (JWT)"
fi
if ! rg -q 'thegraph\.market/dashboard' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must reference The Graph Market dashboard for Token API key management"
fi
for op in getV1Networks getV1EvmTokens getV1EvmBalances; do
if ! rg -q "op" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include op"
fi
done
if rg -q -- 'thegraph-token-mcp-cli (list|describe|call)|--input-json|--args .*\{' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must not use legacy list/describe/call/--input-json/--args JSON forms"
fi
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://token-api\.mcp\.thegraph\.com/' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"The Graph Token MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$thegraph-token-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $thegraph-token-mcp-skill'
fi
echo "skills/thegraph-token-mcp-skill validation passed"
Use The Graph Subgraph MCP through UXC via native SSE with a fixed linked command for subgraph discovery, schema retrieval, deployment selection, and GraphQL...
---
name: thegraph-mcp-skill
description: Use The Graph Subgraph MCP through UXC via native SSE with a fixed linked command for subgraph discovery, schema retrieval, deployment selection, and GraphQL query execution with help-first inspection and explicit auth handling.
---
# The Graph MCP Skill
Use this skill to run The Graph Subgraph MCP operations through `uxc` using the native SSE MCP endpoint.
Reuse the `uxc` skill for generic protocol discovery, envelope parsing, and error handling rules.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://subgraphs.mcp.thegraph.com/sse`.
- The Graph Gateway API key is available for authenticated calls.
## Core Workflow (The Graph-Specific)
Endpoint candidate inputs before finalizing:
- Official remote MCP endpoint:
- `https://subgraphs.mcp.thegraph.com/sse`
1. Verify protocol/path from official source and probe:
- Official source: `https://thegraph.com/docs/en/ai-suite/subgraph-mcp/introduction/`
- probe candidate endpoint with:
- `uxc https://subgraphs.mcp.thegraph.com/sse -h`
- Confirm protocol is MCP (`protocol == "mcp"` in envelope).
2. Detect auth requirement explicitly:
- Run host help or a minimal read call and inspect envelope.
- This endpoint requires a The Graph Gateway API key sent as `Authorization: Bearer <key>`.
3. Register credential and use fixed link command by default:
- `uxc auth credential set thegraph --secret-env THEGRAPH_API_KEY`
- `uxc auth binding add --id thegraph-sse --host subgraphs.mcp.thegraph.com --path-prefix /sse --scheme https --credential thegraph --priority 100`
- `command -v thegraph-mcp-cli`
- If missing, create it:
- `uxc link thegraph-mcp-cli https://subgraphs.mcp.thegraph.com/sse`
- `thegraph-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick another fixed command name.
4. Inspect operation schema before execution:
- `thegraph-mcp-cli -h`
- `thegraph-mcp-cli <operation> -h`
5. Prefer discovery/schema operations first:
- find candidate subgraphs or deployments
- inspect schema
- execute query only after identifier and schema are understood
## Capability Map
- Discovery:
- search subgraphs by keyword
- find top deployments for a contract
- inspect deployment popularity / 30-day query volume
- Schema:
- get schema by deployment ID
- get schema by subgraph ID
- get schema by IPFS hash
- Query execution:
- execute GraphQL query by deployment ID
- execute GraphQL query by subgraph ID
- execute GraphQL query by IPFS hash
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `thegraph-mcp-cli` as default command path.
- `thegraph-mcp-cli <operation> ...` is equivalent to `uxc https://subgraphs.mcp.thegraph.com/sse <operation> ...` when the same auth binding is configured.
- Use direct `uxc "<endpoint>" ...` only as temporary fallback when link setup is unavailable.
- Prefer discovery and schema inspection before query execution.
- For production/stable workflows, prefer deployment-oriented selection over loosely changing latest-version references.
- Keep GraphQL query scope small on first pass:
- add filters
- add limits
- fetch only required fields
- If auth fails:
- confirm `uxc auth credential info thegraph` succeeds
- confirm `uxc auth binding match https://subgraphs.mcp.thegraph.com/sse` resolves to `thegraph`
- rerun `thegraph-mcp-cli -h`
## Tested Real Scenario
The following flow was exercised successfully through `uxc` against the live endpoint:
- search `uniswap` subgraphs
- compare candidates with `get_deployment_30day_query_counts`
- select the highest-volume candidate
- fetch schema for the chosen subgraph
- execute a minimal `_meta` GraphQL query
The selected candidate in that run was:
- subgraph ID: `5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV`
- deployment IPFS hash: `QmTZ8ejXJxRo7vDBS4uwqBeGoxLSWbhaA7oXa1RvxunLy7`
The minimal verified query shape was:
```graphql
{
_meta {
deployment
hasIndexingErrors
}
}
```
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "The Graph MCP"
short_description: "Discover subgraphs, inspect schemas, and run GraphQL via The Graph MCP"
default_prompt: "Use $thegraph-mcp-skill to discover The Graph subgraphs, inspect schemas, choose deployments, and execute GraphQL queries through The Graph Subgraph MCP with help-first inspection and explicit API-key auth."
FILE:references/usage-patterns.md
# Usage Patterns
All commands in this skill use the native MCP SSE endpoint:
`https://subgraphs.mcp.thegraph.com/sse`
This skill defaults to fixed link command `thegraph-mcp-cli`.
Create it when missing:
```bash
command -v thegraph-mcp-cli
uxc auth credential set thegraph --secret-env THEGRAPH_API_KEY
uxc auth binding add --id thegraph-sse --host subgraphs.mcp.thegraph.com --path-prefix /sse --scheme https --credential thegraph --priority 100
uxc link thegraph-mcp-cli https://subgraphs.mcp.thegraph.com/sse
thegraph-mcp-cli -h
```
Notes:
- This skill now uses native SSE support in `uxc`.
- Auth is handled through standard `uxc auth credential` + `binding`.
- Check the active binding with `uxc auth binding match https://subgraphs.mcp.thegraph.com/sse`.
## Discover And Inspect
```bash
thegraph-mcp-cli -h
thegraph-mcp-cli <operation> -h
```
Use help-first inspection to identify the exact operation names exposed by the bridge in your current version.
## Read-First Flow
Recommended workflow:
1. Search candidate subgraphs or deployments
2. Inspect schema for the chosen deployment/subgraph
3. Run a minimal GraphQL query with limited fields and rows
## Example Query Payload Pattern
Most query execution operations accept either key/value fields or positional JSON.
Prefer positional JSON for GraphQL requests with embedded query text:
```bash
thegraph-mcp-cli <query-operation> '{"query":"{ _meta { deployment } }"}'
```
When the operation requires a deployment ID, subgraph ID, or IPFS hash, inspect `-h` first and include only the required identifier plus the GraphQL query.
## Example Discovery Pattern
Search by keyword first, then pivot to schema:
```bash
thegraph-mcp-cli <search-operation> keyword=uniswap
thegraph-mcp-cli <schema-operation> deploymentId=Qm...
```
Contract-oriented discovery:
```bash
thegraph-mcp-cli <contract-discovery-operation> contractAddress=0x...
```
## Practical Rules For GraphQL Through MCP
- Start with `_meta` or a tiny entity selection before issuing wide queries.
- Always add `first`, `where`, or other narrowing arguments on the first pass.
- Prefer deployment-based execution for stable repeated workflows.
- Use subgraph-level selection when you intentionally want the latest version.
## Output Parsing
Rely on envelope fields:
- Success: `ok == true`, consume `data`
- Failure: `ok == false`, inspect `error.code` and `error.message`
## Fallback Equivalence
- `thegraph-mcp-cli <operation> ...` is equivalent to `uxc https://subgraphs.mcp.thegraph.com/sse <operation> ...` when the same auth binding is configured.
- If link setup is temporarily unavailable, use the direct `uxc "<endpoint>" ...` form as fallback.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/thegraph-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/scripts/validate.sh"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*thegraph-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: thegraph-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'subgraphs\.mcp\.thegraph\.com/sse' "SKILL_FILE"; then
fail "SKILL.md must document The Graph MCP endpoint"
fi
if ! rg -q 'command -v thegraph-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link thegraph-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'uxc auth credential set thegraph --secret-env THEGRAPH_API_KEY' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must register a credential for The Graph auth"
fi
if ! rg -q 'uxc auth binding add --id thegraph-sse --host subgraphs\.mcp\.thegraph\.com --path-prefix /sse --scheme https --credential thegraph --priority 100' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include auth binding setup for native The Graph SSE endpoint"
fi
if rg -q 'mcp-remote|inject-env|\$\{THEGRAPH_API_KEY\}' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must not rely on old mcp-remote/env-injection guidance"
fi
if ! rg -q 'thegraph-mcp-cli -h' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must use thegraph-mcp-cli help-first discovery"
fi
if ! rg -q 'THEGRAPH_API_KEY' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include THEGRAPH_API_KEY auth guidance"
fi
if ! rg -q 'official source' "SKILL_FILE"; then
fail "SKILL.md must mention official-source discovery step"
fi
if ! rg -q 'probe candidate endpoint' "SKILL_FILE"; then
fail "SKILL.md must include probe step before finalizing endpoint"
fi
if ! rg -q 'requires a The Graph Gateway API key' "SKILL_FILE"; then
fail "SKILL.md must explicitly document auth detection result"
fi
if rg -q -- 'thegraph-mcp-cli (list|describe|call)|--input-json|--args .*\{' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must not use legacy list/describe/call/--input-json/--args JSON forms"
fi
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://subgraphs\.mcp\.thegraph\.com/sse' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"The Graph MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$thegraph-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $thegraph-mcp-skill'
fi
echo "skills/thegraph-mcp-skill validation passed"
Use Etherscan MCP through UXC for address balance checks, token holder analysis, transaction inspection, and contract lookup tasks. Use when tasks need Ether...
---
name: etherscan-mcp-skill
description: Use Etherscan MCP through UXC for address balance checks, token holder analysis, transaction inspection, and contract lookup tasks. Use when tasks need Etherscan MCP tools for onchain investigation with help-first schema inspection, bearer-key auth, and tier-aware read-first handling.
---
# Etherscan MCP Skill
Use this skill to run Etherscan MCP operations through `uxc`.
Reuse the `uxc` skill for shared protocol discovery, output parsing, and generic auth/binding flows.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://mcp.etherscan.io/mcp`.
- An Etherscan API key is available for authenticated calls.
## Core Workflow
1. Confirm endpoint and protocol with help-first probing:
- `uxc https://mcp.etherscan.io/mcp -h`
- expected unauthenticated behavior today: `401 Unauthorized`
2. Configure credential/binding for repeatable auth:
- `uxc auth credential set etherscan-mcp --auth-type bearer --secret-env ETHERSCAN_API_KEY`
- `uxc auth credential set etherscan-mcp --auth-type bearer --secret-op op://Engineering/etherscan/api-key`
- `uxc auth binding add --id etherscan-mcp --host mcp.etherscan.io --path-prefix /mcp --scheme https --credential etherscan-mcp --priority 100`
3. Use fixed link command by default:
- `command -v etherscan-mcp-cli`
- If missing, create it: `uxc link etherscan-mcp-cli https://mcp.etherscan.io/mcp`
- `etherscan-mcp-cli -h`
4. Inspect operation schema before execution:
- `etherscan-mcp-cli balance -h`
- `etherscan-mcp-cli tokenTopHolders -h`
- `etherscan-mcp-cli getContractAbi -h`
- `etherscan-mcp-cli transaction -h`
5. Prefer read operations first, then any workflow that could trigger heavy data pulls or follow-up automation.
## Capability Map
- Address investigation:
- `balance`
- `tokenHoldings`
- `fundedBy`
- `getAddressMetadata`
- Token holder analysis:
- `balanceERC20`
- `balanceERC20Historical`
- `tokenTopHolders`
- `getTokenInfo`
- Transaction investigation:
- `txList`
- `internalTxsByAddress`
- `erc20Transfers`
- `erc721Transfers`
- `erc1155Transfers`
- `transaction`
- `checkTransaction`
- Contract research:
- `getContractAbi`
- `getContractSourceCode`
- `getContractCreation`
- Verification:
- `verifySourceCode`
- `checkVerifyStatus`
Inspect `etherscan-mcp-cli -h` after auth setup for the current full tool list. Etherscan can expand MCP tools independently of this wrapper skill.
## Recommended Usage Pattern
1. Start from a read-only investigation goal:
- balance or holdings for an address
- holder concentration for a token
- transaction inspection for a hash
- source/metadata lookup for a contract
2. Run `-h` on the specific tool before the first real call.
3. Prefer one chain/address/token at a time before broadening the scope.
4. Parse the JSON envelope first, then inspect `data`.
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `etherscan-mcp-cli` as default command path.
- `etherscan-mcp-cli <operation> ...` is equivalent to `uxc https://mcp.etherscan.io/mcp <operation> ...`.
- If unauthenticated probe or runtime call returns `401 Unauthorized`:
- confirm auth binding matches endpoint with `uxc auth binding match https://mcp.etherscan.io/mcp`
- confirm credential shape with `uxc auth credential info etherscan-mcp`
- reset credential as bearer if needed: `uxc auth credential set etherscan-mcp --auth-type bearer --secret-env ETHERSCAN_API_KEY`
- Use `key=value` only for simple scalar inputs.
- Prefer positional JSON when an operation accepts nested objects, arrays, or optional flags that may evolve.
- Do not assume tool argument names from memory; inspect `<operation> -h` first because Etherscan may revise MCP schemas independently of this skill.
- Some MCP tools are tier-gated by Etherscan plan. Today `getTokenInfo` can return `NOTOK` with an API Pro upgrade message on non-Pro keys.
- Treat `verifySourceCode` as a write-like action requiring explicit user confirmation.
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Etherscan MCP"
short_description: "Onchain reads and contract lookup via Etherscan MCP"
default_prompt: "Use $etherscan-mcp-skill to run Etherscan MCP workflows for address portfolio, token holder, and contract lookup tasks with help-first schema inspection and safe auth handling."
FILE:references/usage-patterns.md
# Usage Patterns
This skill defaults to fixed link command `etherscan-mcp-cli`.
## Setup
```bash
command -v etherscan-mcp-cli
uxc link etherscan-mcp-cli https://mcp.etherscan.io/mcp
etherscan-mcp-cli -h
```
Auth setup:
```bash
uxc auth credential set etherscan-mcp --auth-type bearer --secret-env ETHERSCAN_API_KEY
uxc auth binding add --id etherscan-mcp --host mcp.etherscan.io --path-prefix /mcp --scheme https --credential etherscan-mcp --priority 100
```
Optional secret manager source:
```bash
uxc auth credential set etherscan-mcp --auth-type bearer --secret-op op://Engineering/etherscan/api-key
```
## Help-First Discovery
```bash
etherscan-mcp-cli balance -h
etherscan-mcp-cli tokenTopHolders -h
etherscan-mcp-cli getContractAbi -h
etherscan-mcp-cli transaction -h
```
If the first probe is unauthenticated, `uxc https://mcp.etherscan.io/mcp -h` currently returns `401 Unauthorized`. Configure bearer auth, then retry.
## Address Investigation
Start with operation help, then pass a single address:
```bash
etherscan-mcp-cli balance address=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
```
`balance` also accepts optional `chainid`:
```bash
etherscan-mcp-cli balance '{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","chainid":1}'
```
## Token Holder Analysis
Inspect the exact accepted fields first:
```bash
etherscan-mcp-cli tokenTopHolders -h
etherscan-mcp-cli balanceERC20 -h
```
Real parameter names from current MCP schema:
```bash
etherscan-mcp-cli tokenTopHolders contractaddress=0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 limit=10 sortorder=desc
etherscan-mcp-cli balanceERC20 '{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","contractaddress":"0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","chainid":1}'
```
Tier note:
```bash
etherscan-mcp-cli getTokenInfo contractaddress=0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
```
With the current test key, `getTokenInfo` returned `NOTOK` and an API Pro upgrade message. Expect plan-gated behavior on some tools.
## Contract Research
Check the contract lookup schema first:
```bash
etherscan-mcp-cli getContractAbi -h
etherscan-mcp-cli getContractSourceCode -h
etherscan-mcp-cli getContractCreation -h
```
Then run the lookup with the smallest payload that matches help:
```bash
etherscan-mcp-cli getContractAbi address=0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
etherscan-mcp-cli getContractSourceCode '{"address":"0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","chainid":1}'
etherscan-mcp-cli getContractCreation contractaddresses=0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
```
## Transaction Investigation
Check the schema first:
```bash
etherscan-mcp-cli transaction -h
etherscan-mcp-cli checkTransaction -h
etherscan-mcp-cli txList -h
```
Typical read-only examples:
```bash
etherscan-mcp-cli transaction txhash=0x51453d98c6f7b1c6fd4e2e39d4f10b4a13c7e7f0f6f1f5c2457cb6c58a12f8ab
etherscan-mcp-cli checkTransaction txhash=0x51453d98c6f7b1c6fd4e2e39d4f10b4a13c7e7f0f6f1f5c2457cb6c58a12f8ab
etherscan-mcp-cli txList '{"address":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","startblock":0,"endblock":99999999,"page":1,"offset":5,"sort":"desc"}'
```
## Auth Failure Recovery
When calls fail with `401 Unauthorized`:
```bash
uxc auth binding match https://mcp.etherscan.io/mcp
uxc auth credential info etherscan-mcp
```
If needed, recreate the bearer credential:
```bash
uxc auth credential set etherscan-mcp --auth-type bearer --secret-env ETHERSCAN_API_KEY
```
## Fallback Equivalence
- `etherscan-mcp-cli <operation> ...` is equivalent to `uxc https://mcp.etherscan.io/mcp <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/etherscan-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/scripts/validate.sh"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*etherscan-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: etherscan-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'mcp\.etherscan\.io/mcp' "SKILL_FILE"; then
fail "SKILL.md must document Etherscan MCP endpoint"
fi
if ! rg -q 'command -v etherscan-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link etherscan-mcp-cli https://mcp\.etherscan\.io/mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'etherscan-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use etherscan-mcp-cli help-first discovery"
fi
for op in balance tokenTopHolders getContractAbi transaction; do
if ! rg -q "op" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Etherscan docs must include op"
fi
done
if rg -q -- '--input-json|etherscan-mcp-cli list|etherscan-mcp-cli describe|etherscan-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Etherscan docs must not use list/describe/call/--input-json in default examples"
fi
if ! rg -q 'references/usage-patterns\.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://mcp\.etherscan\.io/mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Etherscan docs must include single-point fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"Etherscan MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$etherscan-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $etherscan-mcp-skill'
fi
echo "skills/etherscan-mcp-skill validation passed"
Use Dune MCP through UXC for blockchain table discovery, SQL query creation/execution, execution result retrieval, and visualization with help-first schema i...
---
name: dune-mcp-skill
description: Use Dune MCP through UXC for blockchain table discovery, SQL query creation/execution, execution result retrieval, and visualization with help-first schema inspection, explicit auth binding, and guarded credit-consuming operations.
---
# Dune MCP Skill
Use this skill to run Dune MCP operations through `uxc`.
Reuse the `uxc` skill for shared protocol discovery, output parsing, and generic auth/binding flows.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://api.dune.com/mcp/v1`.
- Dune API key is available for authenticated calls.
## Core Workflow
1. Confirm endpoint and protocol with help-first probing:
- `uxc https://api.dune.com/mcp/v1 -h`
2. Configure credential/binding for repeatable auth:
- `uxc auth credential set dune-mcp --auth-type api_key --header "x-dune-api-key={{secret}}" --secret-env DUNE_API_KEY`
- `uxc auth credential set dune-mcp --auth-type api_key --header "x-dune-api-key={{secret}}" --secret-op op://Engineering/dune/api-key`
- `uxc auth binding add --id dune-mcp --host api.dune.com --path-prefix /mcp/v1 --scheme https --credential dune-mcp --priority 100`
3. Use fixed link command by default:
- `command -v dune-mcp-cli`
- If missing, create it: `uxc link dune-mcp-cli https://api.dune.com/mcp/v1`
- `dune-mcp-cli -h`
4. Inspect operation schema before execution:
- `dune-mcp-cli searchTables -h`
- `dune-mcp-cli searchTablesByContractAddress -h`
- `dune-mcp-cli createDuneQuery -h`
- `dune-mcp-cli executeQueryById -h`
- `dune-mcp-cli getExecutionResults -h`
5. Prefer read/discovery operations first, then query creation or credit-consuming execution.
## Capability Map
- Discovery:
- `searchDocs`
- `searchTables`
- `listBlockchains`
- `searchTablesByContractAddress`
- Query lifecycle:
- `createDuneQuery`
- `getDuneQuery`
- `updateDuneQuery`
- `executeQueryById`
- `getExecutionResults`
- Analysis helpers:
- `generateVisualization`
- `getTableSize`
- `getUsage`
## Recommended Usage Pattern
1. Find the right table first:
- `dune-mcp-cli searchTables query='uniswap swaps'`
- `dune-mcp-cli searchTablesByContractAddress contractAddress=0x...`
2. Prefer higher-level `spell` tables when they already expose the metrics you need.
3. Keep SQL partition-aware:
- use `block_date`, `evt_block_date`, or another partition/date column in `WHERE`
4. Create a temporary query only after confirming table choice and date range.
5. Execute and fetch results by execution ID.
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `dune-mcp-cli` as default command path.
- `dune-mcp-cli <operation> ...` is equivalent to `uxc https://api.dune.com/mcp/v1 <operation> ...`.
- Discovery operations are read-only:
- `searchDocs`
- `searchTables`
- `listBlockchains`
- `searchTablesByContractAddress`
- `getDuneQuery`
- `getExecutionResults`
- `getTableSize`
- `getUsage`
- Require explicit user confirmation before credit-consuming or state-changing operations:
- `createDuneQuery`
- `updateDuneQuery`
- `executeQueryById`
- `generateVisualization`
- Be careful with privacy:
- confirm before switching a query from private to public
- temporary queries can still be visible; inspect `is_private` and `is_temp`
- `key=value` input now supports automatic type conversion for numeric MCP arguments.
- Numeric IDs can be passed directly with `key=value`, for example:
- `query_id=6794106`
- `queryId=6794106`
- Positional JSON is still useful for nested objects or when mixing string and numeric fields precisely:
- `{"executionId":"01...","timeout":90,"limit":20}`
- For SQL passed via `key=value`, wrap the whole SQL string in double quotes so inner SQL single quotes survive shell parsing.
- If `listBlockchains` returns a Dune-side schema/facet error, fall back to `searchTables` with `blockchains` filters.
## Tested Real Scenario
The following flow was exercised successfully through `uxc`:
- discover table: `uniswap.uniswapx_trades`
- create temporary query for Base daily volume
- execute query
- fetch results
The successful SQL shape was:
```sql
SELECT block_date,
ROUND(SUM(amount_usd), 2) AS daily_volume_usd,
COUNT(*) AS trades
FROM uniswap.uniswapx_trades
WHERE blockchain = 'base'
AND block_date >= date_add('day', -7, CURRENT_DATE)
GROUP BY 1
ORDER BY 1 DESC
LIMIT 7
```
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Dune MCP"
short_description: "Blockchain table discovery, SQL, results, and charts via Dune MCP"
default_prompt: "Use $dune-mcp-skill to run Dune MCP workflows for blockchain table discovery, SQL query creation/execution, result retrieval, and visualization with help-first schema inspection and safe auth handling."
FILE:references/usage-patterns.md
# Usage Patterns
This skill defaults to fixed link command `dune-mcp-cli`.
## Setup
```bash
command -v dune-mcp-cli
uxc link dune-mcp-cli https://api.dune.com/mcp/v1
dune-mcp-cli -h
```
Auth setup:
```bash
uxc auth credential set dune-mcp --auth-type api_key --header "x-dune-api-key={{secret}}" --secret-env DUNE_API_KEY
uxc auth binding add --id dune-mcp --host api.dune.com --path-prefix /mcp/v1 --scheme https --credential dune-mcp --priority 100
```
Optional secret manager source:
```bash
uxc auth credential set dune-mcp --auth-type api_key --header "x-dune-api-key={{secret}}" --secret-op op://Engineering/dune/api-key
```
## Help-First Discovery
```bash
dune-mcp-cli searchTables -h
dune-mcp-cli searchTablesByContractAddress -h
dune-mcp-cli createDuneQuery -h
dune-mcp-cli executeQueryById -h
dune-mcp-cli getExecutionResults -h
```
## Table Discovery
Search by topic:
```bash
dune-mcp-cli searchTables query='uniswap swaps' blockchains=base limit=10
```
Search with schema included:
```bash
dune-mcp-cli searchTables '{"query":"uniswap swaps","blockchains":["base"],"limit":5,"includeSchema":true}'
```
Search decoded tables by contract:
```bash
dune-mcp-cli searchTablesByContractAddress contractAddress=0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
```
If `listBlockchains` fails with a facet/schema error, skip it and continue with `searchTables`.
## Query Lifecycle
Create a temporary query:
```bash
dune-mcp-cli createDuneQuery \
name='tmp uxc test: base uniswapx daily volume' \
query="SELECT block_date, ROUND(SUM(amount_usd), 2) AS daily_volume_usd, COUNT(*) AS trades FROM uniswap.uniswapx_trades WHERE blockchain = 'base' AND block_date >= date_add('day', -7, CURRENT_DATE) GROUP BY 1 ORDER BY 1 DESC LIMIT 7"
```
Fetch query details:
```bash
dune-mcp-cli getDuneQuery query_id=6794106
```
Execute query:
```bash
dune-mcp-cli executeQueryById query_id=6794106
```
Fetch results by execution ID:
```bash
dune-mcp-cli getExecutionResults '{"executionId":"01KK31GEFYA576GN1PC3ZZJNS8","timeout":90,"limit":20}'
```
Update query content:
```bash
dune-mcp-cli updateDuneQuery queryId=6794106 query="SELECT 1"
```
## Practical Rules For SQL
- Prefer `spell` tables before raw decoded tables when they already expose the metric you need.
- Always add a time filter on partition columns such as `block_date` or `evt_block_date`.
- Keep initial result sets small with `LIMIT`.
- For IDs typed as numbers in MCP schema, `key=value` is fine because `uxc` now auto-converts numeric argument types.
- Use positional JSON when you need nested objects or tighter control over mixed types.
- When passing SQL as `key=value`, quote the whole SQL string with double quotes.
## Example: Base UniswapX Daily Volume
This real flow worked through `uxc`:
```bash
dune-mcp-cli searchTables '{"query":"UniswapX trades daily volume","blockchains":["base"],"categories":["spell"],"limit":3,"includeSchema":true}'
dune-mcp-cli createDuneQuery \
name='tmp uxc test: base uniswapx daily volume final 2026-03-07' \
query="SELECT block_date, ROUND(SUM(amount_usd), 2) AS daily_volume_usd, COUNT(*) AS trades FROM uniswap.uniswapx_trades WHERE blockchain = 'base' AND block_date >= date_add('day', -7, CURRENT_DATE) GROUP BY 1 ORDER BY 1 DESC LIMIT 7"
dune-mcp-cli executeQueryById '{"query_id":6794110}'
dune-mcp-cli getExecutionResults '{"executionId":"01KK31GEFYA576GN1PC3ZZJNS8","timeout":90,"limit":20}'
```
Representative result rows:
- `2026-03-06` -> `2148623.85` USD, `380` trades
- `2026-03-04` -> `5014661.68` USD, `736` trades
## Visualization And Usage
Check usage before heavy experimentation:
```bash
dune-mcp-cli getUsage
```
Generate a chart only after confirming the query results and column names:
```bash
dune-mcp-cli generateVisualization -h
```
Run `generateVisualization` only with explicit user approval because it creates saved artifacts.
## Fallback Equivalence
- `dune-mcp-cli <operation> ...` is equivalent to `uxc https://api.dune.com/mcp/v1 <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/dune-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/scripts/validate.sh"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*dune-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: dune-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'api\.dune\.com/mcp/v1' "SKILL_FILE"; then
fail "SKILL.md must document Dune MCP endpoint"
fi
if ! rg -q 'command -v dune-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link dune-mcp-cli https://api\.dune\.com/mcp/v1' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'dune-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use dune-mcp-cli help-first discovery"
fi
for op in searchTables createDuneQuery executeQueryById getExecutionResults; do
if ! rg -q "op" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Dune docs must include op"
fi
done
if rg -q -- '--input-json|dune-mcp-cli list|dune-mcp-cli describe|dune-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Dune docs must not use list/describe/call/--input-json in default examples"
fi
if ! rg -q 'references/usage-patterns\.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://api\.dune\.com/mcp/v1' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Dune docs must include single-point fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"Dune MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$dune-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $dune-mcp-skill'
fi
echo "skills/dune-mcp-skill validation passed"
Operate Discord HTTP API through UXC with Discord OpenAPI schema. Bot token recommended for full API access including messages and server management. OAuth2...
---
name: discord-openapi-skill
description: Operate Discord HTTP API through UXC with Discord OpenAPI schema. Bot token recommended for full API access including messages and server management. OAuth2 user authentication available for limited profile operations only.
---
# Discord API Skill
Use this skill to run Discord REST operations through `uxc` + OpenAPI.
Reuse the `uxc` skill for shared execution, auth, and error-handling guidance.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://discord.com/api/v10`.
- Access to Discord OpenAPI spec URL:
- `https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json`
- Discord bot token (recommended) or OAuth2 user authentication (limited functionality).
## Authentication
### Option 1: Bot Token (Recommended)
Bot token provides full access to Discord API including reading messages, managing servers, sending messages, and all administrative operations. This is the recommended method for most use cases.
1. Create a bot application at https://discord.com/developers/applications
2. Generate a bot token from the Bot section
3. Configure bot credential:
```bash
uxc auth credential set discord-bot \
--auth-type api_key \
--header "Authorization=Bot {{secret}}" \
--secret "YOUR_BOT_TOKEN_HERE"
```
4. Bind credential to Discord API endpoint:
```bash
uxc auth binding add \
--id discord-bot \
--host discord.com \
--path-prefix /api/v10 \
--scheme https \
--credential discord-bot \
--priority 100
```
### Option 2: OAuth2 User Authentication (Limited Use Cases)
**Important:** User OAuth2 has significant limitations and is **not recommended** for most operations:
- ❌ Cannot read channel messages via HTTP API (local RPC only)
- ❌ Cannot send messages or manage servers
- ✅ Can read user profile, email, connections
- ✅ Can list user's servers
Only use OAuth2 if you specifically need to access user profile information as the user. For all other operations, use Bot Token.
If you still need OAuth2 for user profile operations:
**Configuration:**
- Client ID: `1479302369723285736`
- Redirect URI: `http://127.0.0.1:11111/callback`
**OAuth2 Scopes:**
Discord user OAuth2 supports **read-only operations**. It cannot send messages or manage servers as a user (use Bot Token for those operations).
**Recommended Scopes (Full Functionality):**
```bash
--scope "identify email connections guilds guilds.members.read messages.read openid"
```
**Minimal Read-Only Scopes:**
```bash
--scope "identify email connections guilds guilds.members.read"
```
**Scope Reference:**
| Scope | Description | Write Operation |
|-------|-------------|-----------------|
| `identify` | Basic user info (username, avatar, etc.) | ❌ Read |
| `email` | User's email address | ❌ Read |
| `connections` | Linked third-party accounts (Twitch, YouTube, etc.) | ❌ Read |
| `guilds` | User's server list | ❌ Read |
| `guilds.join` | Join user to servers (requires the same application's bot to already be in that guild) | ✅ **Write** |
| `guilds.members.read` | User's member info in servers | ❌ Read |
| `messages.read` | Read messages (local RPC only, **not** HTTP API) | ❌ Read |
| `openid` | OpenID Connect support | ❌ Read |
**Note:** User OAuth2 **cannot** send messages or manage servers as the user. Use Bot Token for write operations. `guilds.join` is a special user OAuth write capability that depends on the same application's bot already being in the target guild, so it is not part of the default read-only flow. See [Discord OAuth2 documentation](https://discord.com/developers/topics/oauth2) for complete scope list.
**Two-Stage OAuth Flow (Agent-Friendly):**
1. Start OAuth flow with desired scopes:
```bash
uxc auth oauth start discord-user \
--endpoint https://discord.com/api/oauth2/token \
--client-id 1479302369723285736 \
--redirect-uri http://127.0.0.1:11111/callback \
--scope "identify email connections guilds guilds.members.read messages.read openid"
```
2. Open the displayed authorization URL in browser, complete authorization, then copy the callback URL from browser address bar.
3. Complete OAuth flow:
```bash
uxc auth oauth complete discord-user \
--session-id <session_id_from_step_1> \
--authorization-response "<callback_url_from_browser>"
```
4. Bind credential:
```bash
uxc auth binding add \
--id discord-user \
--host discord.com \
--path-prefix /api/v10 \
--scheme https \
--credential discord-user \
--priority 100
```
**Interactive Alternative (Local Terminal Only):**
```bash
uxc auth oauth login discord-user \
--endpoint https://discord.com/api/oauth2/token \
--flow authorization_code \
--client-id 1479302369723285736 \
--redirect-uri http://127.0.0.1:11111/callback \
--scope "identify email connections guilds guilds.members.read messages.read openid"
```
Then paste the callback URL when prompted.
## Core Workflow
1. Use fixed link command by default:
- `command -v discord-openapi-cli`
- If missing, create it: `uxc link discord-openapi-cli https://discord.com/api/v10 --schema-url https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json`
- `discord-openapi-cli -h`
2. Discover operations with schema mapping:
- `discord-openapi-cli -h`
3. Inspect operation schema first:
- `discord-openapi-cli get:/users/@me -h`
- `discord-openapi-cli post:/channels/{channel_id}/messages -h`
4. Execute operation:
- connectivity check (no auth): `discord-openapi-cli get:/gateway`
- key/value: `discord-openapi-cli get:/guilds/{guild_id}/channels guild_id=GUILD_ID`
- positional JSON: `discord-openapi-cli post:/channels/{channel_id}/messages '{"channel_id":"CHANNEL_ID","content":"Hello from uxc"}'`
- binding check when auth looks wrong: `uxc auth binding match https://discord.com/api/v10`
## Authentication Methods Comparison
| Feature | Bot Token | User OAuth2 |
|---------|-----------|-------------|
| **Read channel messages** | ✅ Full access | ❌ Not via HTTP API |
| **Send messages** | ✅ As the bot | ❌ Not supported |
| **Manage channels/roles** | ✅ Bot permissions | ❌ Not supported |
| **Moderation actions** | ✅ Bot permissions | ❌ Not supported |
| **List servers** | ✅ Servers bot is in | ✅ User's servers |
| **Read user info** | ❌ Not available | ✅ As the user |
| **Message appearance** | Bot badge "BOT" | N/A |
**Key Recommendation:** Use **Bot Token** for almost all operations. User OAuth2 is only useful if you need to read user profile information as that specific user. For reading channel messages, managing servers, or sending messages, Bot Token is required.
## Subscribe / Gateway Status
Discord inbound events flow through the Gateway WebSocket, not through this REST/OpenAPI surface.
Current `uxc subscribe` status:
- the built-in `discord-gateway` transport now bootstraps through `GET /gateway/bot`
- live Gateway sessions reached `READY` and delivered `GUILD_CREATE`
- a real posted channel message produced a `MESSAGE_CREATE` event in the subscribe sink
- heartbeat scheduling, `IDENTIFY`, sequence tracking, and reconnect handling are implemented
Recommended invocation:
```bash
uxc subscribe start https://discord.com/api/v10 \
'{"intents":4609,"device":"uxc-discord"}' \
--transport discord-gateway \
--auth discord-bot \
--sink file:$HOME/.uxc/subscriptions/discord-gateway.ndjson
```
Intent notes:
- `4609` = `GUILDS | GUILD_MESSAGES | DIRECT_MESSAGES`
- add `32768` (`MESSAGE_CONTENT`) only when the bot has that privileged intent enabled in the Discord developer portal
Use `discord-openapi-cli` for REST calls and `uxc subscribe start ... --transport discord-gateway ...` for inbound Gateway events.
## Guardrails
- **OAuth2 Scope Limitation:** User OAuth2 tokens cannot read channel messages via HTTP API, send messages, or manage servers. These operations require Bot Token authentication.
- Discord OpenAPI spec is persisted in the generated link via `uxc link --schema-url ...`; pass `--schema-url <other-url>` only when you need to override it temporarily.
- Keep automation on JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Prefer positional JSON for non-string objects instead of `--input-json`.
- `discord-openapi-cli <operation> ...` is equivalent to `uxc https://discord.com/api/v10 --schema-url <discord_openapi_spec> <operation> ...`.
- Treat `post:/channels/{channel_id}/messages`, delete/update endpoints, and moderation endpoints as write/high-risk operations; require explicit user confirmation before execution.
## References
- Usage patterns: `references/usage-patterns.md`
- Discord API docs: https://discord.com/developers/docs
- Discord API OpenAPI spec: https://github.com/discord/discord-api-spec
FILE:agents/openai.yaml
interface:
display_name: "Discord API"
short_description: "Operate Discord REST API via UXC + OpenAPI schema mapping"
default_prompt: "Use $discord-openapi-skill to discover and execute Discord REST operations through UXC with --schema-url and bot-token auth guardrails."
FILE:references/usage-patterns.md
# Discord API Skill - Usage Patterns
## Link Setup
```bash
command -v discord-openapi-cli
uxc link discord-openapi-cli https://discord.com/api/v10 \
--schema-url https://raw.githubusercontent.com/discord/discord-api-spec/main/specs/openapi.json
discord-openapi-cli -h
```
## Auth Setup (Bot Token)
```bash
uxc auth credential set discord-bot \
--auth-type api_key \
--header "Authorization=Bot {{secret}}" \
--secret-env DISCORD_BOT_TOKEN
uxc auth binding add \
--id discord-bot \
--host discord.com \
--path-prefix /api/v10 \
--scheme https \
--credential discord-bot \
--priority 100
```
## Read Examples
```bash
# Connectivity check (public endpoint)
discord-openapi-cli get:/gateway
# Get current bot/application user
discord-openapi-cli get:/users/@me
# List channels in a guild
discord-openapi-cli get:/guilds/{guild_id}/channels guild_id=YOUR_GUILD_ID
```
## Write Example (Confirm Intent First)
```bash
# Create a channel message
discord-openapi-cli post:/channels/{channel_id}/messages '{"channel_id":"YOUR_CHANNEL_ID","content":"Hello from UXC"}'
```
## Discord Gateway Subscribe
Use the bot token with `uxc subscribe` directly. The linked REST command is not the subscribe entrypoint.
```bash
uxc subscribe start https://discord.com/api/v10 \
'{"intents":4609,"device":"uxc-discord"}' \
--transport discord-gateway \
--auth discord-bot \
--sink file:$HOME/.uxc/subscriptions/discord-gateway.ndjson
```
Intent notes:
- `4609` = `GUILDS | GUILD_MESSAGES | DIRECT_MESSAGES`
- add `32768` (`MESSAGE_CONTENT`) only if the bot has that privileged intent enabled
Live validation has confirmed:
- `GET /gateway/bot` bootstrap succeeded
- Gateway `READY` / `GUILD_CREATE` events arrived
- a real `MESSAGE_CREATE` event was captured after posting a channel message
## Fallback Equivalence
- `discord-openapi-cli <operation> ...` is equivalent to
`uxc https://discord.com/api/v10 --schema-url <discord_openapi_spec> <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/discord-openapi-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
rg -q '^name:\s*discord-openapi-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v discord-openapi-cli' "SKILL_FILE" || fail 'missing link-first command check'
rg -q 'uxc link discord-openapi-cli https://discord.com/api/v10 --schema-url ' "SKILL_FILE" || fail 'missing fixed link create command with schema-url'
rg -q 'discord-openapi-cli -h' "SKILL_FILE" || fail 'missing help-first host discovery example'
rg -q 'discord-openapi-cli get:/users/@me -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q 'Authorization=Bot \{\{secret\}\}' "SKILL_FILE" || fail 'missing Discord bot auth header format'
rg -q 'uxc auth binding match https://discord.com/api/v10' "SKILL_FILE" || fail 'missing binding match check'
rg -q 'positional JSON' "SKILL_FILE" || fail 'missing positional JSON guidance'
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail 'found banned legacy JSON argument pattern'
fi
rg -q '^\s*display_name:\s*"Discord API"\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$discord-openapi-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $discord-openapi-skill'
echo "skills/discord-openapi-skill validation passed"
Operate Linear workspace issues, projects, and teams through Linear GraphQL API using UXC. Use when tasks require querying or creating issues, managing proje...
---
name: linear-graphql-skill
description: Operate Linear workspace issues, projects, and teams through Linear GraphQL API using UXC. Use when tasks require querying or creating issues, managing projects, or interacting with Linear workflow. Supports both Personal API Key and OAuth authentication.
---
# Linear GraphQL Skill
Use this skill to run Linear GraphQL API operations through `uxc`.
Reuse the `uxc` skill guidance for discovery, schema inspection, auth lifecycle, and error recovery.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://api.linear.app/graphql`.
- Linear API key or OAuth credentials available.
## Authentication
Linear supports two authentication methods:
### Option 1: Personal API Key (Recommended for development)
1. Get your API key from Linear: https://linear.app/settings/api
2. Set credential with custom Authorization header:
```bash
uxc auth credential set linear-graphql \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret "lin_api_XXX"
```
Or use environment variable:
```bash
export LINEAR_API_KEY="lin_api_XXX"
uxc auth credential set linear-graphql \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret-env LINEAR_API_KEY
```
3. Bind endpoint:
```bash
uxc auth binding add \
--id linear-graphql \
--host api.linear.app \
--path-prefix /graphql \
--scheme https \
--credential linear-graphql \
--priority 100
```
**Important (Personal API Key only):** Linear API keys require `Authorization: lin_api_XXX` format (no `Bearer ` prefix). The `--header "Authorization:{{secret}}"` configuration above is required for API key auth. For OAuth access tokens, use standard `Authorization: Bearer <token>` semantics (handled by the OAuth credential flow in `uxc`).
Credential/binding IDs in this skill use `linear-graphql` by convention; IDs are local aliases and can be changed if needed.
### Option 2: OAuth 2.0 (For production/user-delegated access)
1. Create an OAuth app in Linear: https://linear.app/settings/api
2. Start OAuth login:
```bash
uxc auth oauth start linear-graphql \
--endpoint https://api.linear.app/graphql \
--redirect-uri http://127.0.0.1:8788/callback \
--scope read \
--scope write
```
After approval, complete it with:
```bash
uxc auth oauth complete linear-graphql \
--session-id <session_id> \
--authorization-response 'http://127.0.0.1:8788/callback?code=...&state=...'
```
3. Bind endpoint:
```bash
uxc auth binding add \
--id linear-graphql \
--host api.linear.app \
--path-prefix /graphql \
--scheme https \
--credential linear-graphql \
--priority 100
```
## Core Workflow
1. Use fixed link command by default:
- `command -v linear-graphql-cli`
- If the command exists, confirm it is the expected `uxc link` for this skill before using it.
- If a command conflict is detected and cannot be safely reused, stop and ask skill maintainers for guidance instead of dynamically renaming the command.
- If missing, create it: `uxc link linear-graphql-cli https://api.linear.app/graphql`
- `linear-graphql-cli -h`
2. Discover operations:
- `linear-graphql-cli -h`
- Returns hundreds of GraphQL operations
3. Inspect specific operation:
- `linear-graphql-cli query/issues -h`
- `linear-graphql-cli mutation/issueCreate -h`
4. Execute queries:
```bash
# Query issues (simple)
linear-graphql-cli query/issues '{"first":10}'
# Query issues with explicit selection set for useful fields
linear-graphql-cli query/issues '{"first":10,"_select":"nodes { identifier title url state { name } assignee { name } }"}'
# Query teams
linear-graphql-cli query/teams '{"first":10}'
# Create issue (requires write scope)
linear-graphql-cli mutation/issueCreate '{
"input": {
"teamId": "TEAM_ID",
"title": "New Issue Title",
"description": "Issue description"
}
}'
```
## Available Operations
### Queries
- `query/issues` - List and filter issues
- `query/issue` - Get single issue
- `query/teams` - List teams
- `query/team` - Get single team
- `query/projects` - List projects
- `query/workflowStates` - List workflow states
### Mutations
- `mutation/issueCreate` - Create new issue
- `mutation/issueUpdate` - Update issue
- `mutation/issueArchive` - Archive issue
- `mutation/commentCreate` - Add comment
## Usage Examples
### List recent issues
```bash
linear-graphql-cli query/issues '{"first":20,"_select":"nodes { identifier title url state { name } assignee { name } }"}'
```
### Get issue by ID
```bash
linear-graphql-cli query/issue id=ISSUE_ID
```
### List teams
```bash
linear-graphql-cli query/teams
```
### Create issue
```bash
linear-graphql-cli mutation/issueCreate '{"input":{"teamId":"YOUR_TEAM_ID","title":"Fix bug"}}'
```
## Troubleshooting
### Authentication Errors
**Error: "Bearer token" prefix rejected**
- Linear API does not accept `Authorization: Bearer lin_api_XXX`
- Ensure credential uses `--auth-type api_key --header "Authorization:{{secret}}"`
- Do not use `--auth-type bearer`
**Error: "Credential not found"**
- Check credential exists: `uxc auth credential list`
- Verify binding: `uxc auth binding list`
- Create binding if missing (see Authentication section)
**OAuth login spans multiple agent turns**
- Prefer `uxc auth oauth start ...` and `uxc auth oauth complete ...`
- Use `uxc auth oauth login ... --flow authorization_code` only when one process can wait for the pasted callback URL
**Error: "No binding matched"**
- Check binding exists: `uxc auth binding match api.linear.app/graphql`
- If missing, create binding with `uxc auth binding add` (see Authentication section)
For detailed authentication troubleshooting, see `uxc` skill's `references/auth-configuration.md`.
### Common Issues
**Daemon issues after credential changes**
- Restart daemon: `uxc daemon restart`
- Check status: `uxc daemon status`
**Environment variable not found**
- Ensure variable is exported in daemon's environment
- Or use `--secret` for literal values (less secure)
- Or use `--secret-op` for 1Password (most secure)
## Guardrails
- Keep automation on JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `data`, `error`.
- Prefer positional JSON for non-string and typed arguments (for example: `linear-graphql-cli query/issues '{"first":10}'` and `linear-graphql-cli mutation/issueCreate '{"input":{"teamId":"TEAM_ID","title":"Test"}}'`).
- Use reserved GraphQL argument `_select` (string) when you need explicit return fields, e.g. `{"_select":"nodes { identifier title }"}`.
- Use `linear-graphql-cli` as the default command path.
- `linear-graphql-cli <operation> ...` is equivalent to `uxc https://api.linear.app/graphql <operation> ...`.
- Prefer read operations first (query/*), then write operations (mutation/*).
- For write operations, always confirm user intent before execution.
- If auth fails, check credential with `uxc auth credential info linear-graphql`.
## References
- Linear API Documentation: https://developers.linear.app
- GraphQL Schema: https://studio.apollographql.com/public/Linear-API
- Invocation patterns: `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: Linear GraphQL Agent
short_description: Interact with Linear issues, teams, and projects via the Linear GraphQL skill.
default_prompt: "Use $linear-graphql-skill to query and update Linear via UXC GraphQL operations, preferring read-first flow and guarded writes."
FILE:references/usage-patterns.md
# Linear GraphQL Skill - Usage Patterns
## Authentication Setup
### Personal API Key (Recommended)
```bash
# Set credential with environment variable
uxc auth credential set linear-graphql \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret-env LINEAR_API_KEY
# Or with literal secret (not recommended for security)
uxc auth credential set linear-graphql \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret lin_api_xxxx
```
### OAuth Flow
```bash
# Start OAuth login
uxc auth oauth start linear-graphql \
--endpoint https://api.linear.app/graphql \
--redirect-uri http://127.0.0.1:8788/callback \
--scope read \
--scope write
# After user approves, complete exchange
uxc auth oauth complete linear-graphql \
--session-id <session_id> \
--authorization-response 'http://127.0.0.1:8788/callback?code=...&state=...'
# Then bind endpoint
uxc auth binding add \
--id linear-graphql \
--host api.linear.app \
--path-prefix /graphql \
--scheme https \
--credential linear-graphql \
--priority 100
```
`uxc auth oauth login linear-graphql ... --flow authorization_code` is still available as a single-process interactive fallback.
## Link Setup
```bash
# Create link command
uxc link linear-graphql-cli https://api.linear.app/graphql
# Verify
linear-graphql-cli -h
```
## Query Examples
### List Issues
```bash
linear-graphql-cli query/issues '{"first":20}'
```
### List Issues With Explicit Fields
```bash
linear-graphql-cli query/issues '{"first":20,"_select":"nodes { identifier title url state { name } assignee { name } }"}'
```
### Filter Issues by Team
```bash
linear-graphql-cli query/issues filter='{"team":{"id":{"eq":"TEAM_ID"}}}'
```
### Get Single Issue
```bash
linear-graphql-cli query/issue id=ISSUE_123
```
### List Teams
```bash
linear-graphql-cli query/teams
```
### List Projects
```bash
linear-graphql-cli query/projects '{"first":10}'
```
## Mutation Examples
### Create Issue
```bash
linear-graphql-cli mutation/issueCreate '{
"input": {
"teamId": "TEAM_ID",
"title": "New Feature Request",
"description": "Description here",
"priority": 2
}
}'
```
### Update Issue
```bash
linear-graphql-cli mutation/issueUpdate '{
"id": "ISSUE_ID",
"input": {
"title": "Updated Title",
"description": "Updated description"
}
}'
```
### Archive Issue
```bash
linear-graphql-cli mutation/issueArchive id=ISSUE_ID
```
### Add Comment
```bash
linear-graphql-cli mutation/commentCreate '{
"input": {
"issueId": "ISSUE_ID",
"body": "Comment body"
}
}'
```
## Error Handling
### Invalid API Key
```
{"ok": false, "error": {"code": "UNAUTHENTICATED", "message": "API key invalid"}}
```
Fix: Check or regenerate API key at https://linear.app/settings/api
### Rate Limiting
```
{"ok": false, "error": {"code": "RATE_LIMITED", "message": "Too many requests"}}
```
Fix: Wait and retry, or reduce request frequency
### Invalid Operation
```
{"ok": false, "error": {"code": "INVALID_ARGUMENT", "message": "Invalid issue ID"}}
```
Fix: Verify the ID format and existence
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/linear-graphql-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
rg -q '^name:\s*linear-graphql-skill\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v linear-graphql-cli' "SKILL_FILE" || fail 'missing link-first command check'
rg -q 'uxc link linear-graphql-cli https://api.linear.app/graphql' "SKILL_FILE" || fail 'missing fixed link create command'
rg -q 'linear-graphql-cli -h' "SKILL_FILE" || fail 'missing host help example'
rg -q 'linear-graphql-cli query/issues -h' "SKILL_FILE" || fail 'missing operation-level help example'
rg -q 'positional JSON' "SKILL_FILE" || fail 'missing positional JSON guidance'
rg -q '_select' "SKILL_FILE" || fail 'missing _select guidance'
rg -Fq 'Authorization:{{secret}}' "SKILL_FILE" || fail 'missing api_key auth header guidance'
if rg -q -- '--input-json' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail 'found banned --input-json pattern'
fi
if rg -q -- "--args\\s+'\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail 'found banned legacy JSON via --args pattern'
fi
rg -q '^\s*display_name:\s*"?Linear GraphQL Agent"?\s*$' "OPENAI_FILE" || fail 'missing display_name'
rg -q '^\s*short_description:\s*.+$' "OPENAI_FILE" || fail 'missing short_description'
rg -q '^\s*default_prompt:\s*".*\$linear-graphql-skill.*"\s*$' "OPENAI_FILE" || fail 'default_prompt must mention $linear-graphql-skill'
echo "skills/linear-graphql-skill validation passed"
Use OKX OnchainOS MCP through UXC for token discovery, market data, wallet balance, and swap execution planning. Use when tasks need OKX MCP tools such as to...
---
name: okx-mcp-skill
description: Use OKX OnchainOS MCP through UXC for token discovery, market data, wallet balance, and swap execution planning. Use when tasks need OKX MCP tools such as token search/ranking/holder, price/trades/candlesticks/index, balance queries, and DEX quote/swap flows with help-first schema inspection and safe auth handling.
---
# OKX MCP Skill
Use this skill to run OKX MCP operations through `uxc`.
Reuse the `uxc` skill for shared protocol discovery, output parsing, and generic auth/binding flows.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://web3.okx.com/api/v1/onchainos-mcp`.
- OKX Onchain API key is available for real calls.
## Quick Trial Key
For quick read-only trial, OKX docs currently expose a shared demo key:
- `d573a84c-8e79-4a35-b0c6-427e9ad2478d`
Example:
- `uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret d573a84c-8e79-4a35-b0c6-427e9ad2478d`
Use your own key for regular usage, stability, and production workflows.
## Core Workflow
1. Confirm endpoint and protocol with help-first probing:
- `uxc https://web3.okx.com/api/v1/onchainos-mcp -h`
2. Configure credential/binding for repeatable auth:
- `uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret-env OKX_ACCESS_KEY`
- `uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret-op op://Engineering/okx/OK-ACCESS-KEY`
- `uxc auth binding add --id okx-mcp --host web3.okx.com --path-prefix /api/v1/onchainos-mcp --scheme https --credential okx-mcp --priority 100`
3. Use fixed link command by default:
- `command -v okx-mcp-cli`
- If missing, create it: `uxc link okx-mcp-cli https://web3.okx.com/api/v1/onchainos-mcp`
- `okx-mcp-cli -h`
4. Inspect operation schema before execution:
- `okx-mcp-cli dex-okx-market-price -h`
- `okx-mcp-cli dex-okx-market-token-search -h`
- `okx-mcp-cli dex-okx-balance-total-value -h`
- `okx-mcp-cli dex-okx-dex-quote -h`
5. Prefer read operations first, then high-impact operations.
## Capability Map
- Market:
- `dex-okx-market-price`
- `dex-okx-market-trades`
- `dex-okx-market-candlesticks`
- `dex-okx-market-candlesticks-history`
- `dex-okx-index-current-price`
- `dex-okx-index-historical-price`
- Token discovery/enrichment:
- `dex-okx-market-token-search`
- `dex-okx-market-token-ranking`
- `dex-okx-market-token-holder`
- `dex-okx-market-token-basic-info`
- `dex-okx-market-token-price-info`
- Wallet:
- `dex-okx-balance-chains`
- `dex-okx-balance-total-value`
- `dex-okx-balance-total-token-balances`
- `dex-okx-balance-specific-token-balance`
- Swap:
- `dex-okx-dex-aggregator-supported-chains`
- `dex-okx-dex-liquidity`
- `dex-okx-dex-quote`
- `dex-okx-dex-approve-transaction`
- `dex-okx-dex-swap`
- `dex-okx-dex-solana-swap-instruction`
## ChainIndex Quick Reference
- Ethereum: `1`
- BSC: `56`
- XLayer: `196`
- Solana: `501`
- Base: `8453`
- Arbitrum: `42161`
Always prefer runtime discovery (`dex-okx-market-price-chains` / `dex-okx-dex-aggregator-supported-chains` / `dex-okx-balance-chains`) when possible.
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `okx-mcp-cli` as default command path.
- `okx-mcp-cli <operation> ...` is equivalent to `uxc https://web3.okx.com/api/v1/onchainos-mcp <operation> ...`.
- If call result contains `Request header OK-ACCESS-KEY can not be empty`:
- confirm auth binding matches endpoint with `uxc auth binding match https://web3.okx.com/api/v1/onchainos-mcp`
- update credential with explicit header: `uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret-env OKX_ACCESS_KEY`
- confirm credential has a secret source (`--secret-env`, `--secret-op`, or literal `--secret`)
- For high-impact operations require explicit user confirmation:
- `dex-okx-dex-approve-transaction`
- `dex-okx-dex-swap`
- `dex-okx-dex-solana-swap-instruction`
- Solana caveat from OKX docs:
- for market candles/trades, use wSOL token address `So11111111111111111111111111111111111111112`
- for some price contexts, SOL system address `11111111111111111111111111111111` may also appear in docs
- when data is empty on Solana, verify operation-level address expectation first.
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "OKX MCP"
short_description: "Token, market, wallet, and swap via OKX MCP"
default_prompt: "Use $okx-mcp-skill to run OKX MCP workflows for token discovery, market data, wallet balance, and swap planning/execution with help-first schema inspection and safe auth handling."
FILE:references/usage-patterns.md
# Usage Patterns
This skill defaults to fixed link command `okx-mcp-cli`.
## Setup
```bash
command -v okx-mcp-cli
uxc link okx-mcp-cli https://web3.okx.com/api/v1/onchainos-mcp
okx-mcp-cli -h
```
Quick trial auth (shared doc key):
```bash
uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret d573a84c-8e79-4a35-b0c6-427e9ad2478d
uxc auth binding add --id okx-mcp --host web3.okx.com --path-prefix /api/v1/onchainos-mcp --scheme https --credential okx-mcp --priority 100
```
For long-term usage, replace with your own key and keep explicit header config:
```bash
uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret-env OKX_ACCESS_KEY
uxc auth binding add --id okx-mcp --host web3.okx.com --path-prefix /api/v1/onchainos-mcp --scheme https --credential okx-mcp --priority 100
```
## Help-First Discovery
```bash
okx-mcp-cli dex-okx-market-price-chains
okx-mcp-cli dex-okx-dex-aggregator-supported-chains
okx-mcp-cli dex-okx-balance-chains
okx-mcp-cli dex-okx-market-token-search -h
okx-mcp-cli dex-okx-dex-quote -h
```
Common chainIndex quick picks:
- `1` (Ethereum), `56` (BSC), `196` (XLayer), `501` (Solana), `8453` (Base), `42161` (Arbitrum)
## Token Discovery
```bash
okx-mcp-cli dex-okx-market-token-search chains=1 search=USDC
okx-mcp-cli dex-okx-market-token-ranking chains=1 sortBy=priceChange timeFrame=24h
okx-mcp-cli dex-okx-market-token-holder chainIndex=1 tokenContractAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
```
## Market Data
```bash
okx-mcp-cli dex-okx-market-price '{"items":[{"chainIndex":"1","tokenContractAddress":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"}]}'
okx-mcp-cli dex-okx-market-trades chainIndex=1 tokenContractAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 limit=50
okx-mcp-cli dex-okx-market-candlesticks-history '{"chainIndex":"1","tokenContractAddress":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48","bar":"1H","limit":"100"}'
```
## Wallet
```bash
okx-mcp-cli dex-okx-balance-total-value address=0xYourAddress chains=1,8453
okx-mcp-cli dex-okx-balance-total-token-balances address=0xYourAddress chains=1,8453
okx-mcp-cli dex-okx-balance-specific-token-balance '{"address":"0xYourAddress","tokenContractAddresses":[{"chainIndex":"1","tokenContractAddress":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"}]}'
```
## Swap (Confirm First)
```bash
okx-mcp-cli dex-okx-dex-quote chainIndex=1 fromTokenAddress=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 toTokenAddress=0xdac17f958d2ee523a2206206994597c13d831ec7 amount=1000000 swapMode=exactIn
```
Run `dex-okx-dex-approve-transaction`, `dex-okx-dex-swap`, or `dex-okx-dex-solana-swap-instruction` only with explicit user approval.
## Auth/Header Failure Recovery
When response includes `Request header OK-ACCESS-KEY can not be empty`:
```bash
uxc auth binding match https://web3.okx.com/api/v1/onchainos-mcp
uxc auth credential info okx-mcp
```
If needed, reset credential with explicit header:
```bash
uxc auth credential set okx-mcp --auth-type api_key --api-key-header OK-ACCESS-KEY --secret-env OKX_ACCESS_KEY
```
## Fallback Equivalence
- `okx-mcp-cli <operation> ...` is equivalent to `uxc https://web3.okx.com/api/v1/onchainos-mcp <operation> ...`.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/okx-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*okx-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: okx-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'web3\.okx\.com/api/v1/onchainos-mcp' "SKILL_FILE"; then
fail "SKILL.md must document OKX MCP endpoint"
fi
if ! rg -q 'command -v okx-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link okx-mcp-cli https://web3\.okx\.com/api/v1/onchainos-mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'okx-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use okx-mcp-cli help-first discovery"
fi
for op in dex-okx-market-price dex-okx-market-token-search dex-okx-balance-total-value dex-okx-dex-quote; do
if ! rg -q "op" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "OKX docs must include op"
fi
done
if rg -q -- '--input-json|okx-mcp-cli list|okx-mcp-cli describe|okx-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "OKX docs must not use list/describe/call/--input-json in default examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "OKX docs must not pass raw JSON via --args"
fi
if ! rg -q 'references/usage-patterns\.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc https://web3\.okx\.com/api/v1/onchainos-mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "OKX docs must include single-point fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"OKX MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$okx-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $okx-mcp-skill'
fi
echo "skills/okx-mcp-skill validation passed"
Ask questions and read documentation about any GitHub repository using DeepWiki MCP. Use when you need to understand a codebase, find specific APIs, or get c...
---
name: deepwiki-mcp-skill
description: Ask questions and read documentation about any GitHub repository using DeepWiki MCP. Use when you need to understand a codebase, find specific APIs, or get context about a repository.
metadata:
short-description: Query GitHub repo docs via DeepWiki
---
# DeepWiki Skill
Use this skill to query GitHub repository documentation and ask questions about codebases.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `mcp.deepwiki.com/mcp`
Note: Repositories must be indexed on DeepWiki first. Visit https://deepwiki.com to index a repository.
### Install uxc
Choose one of the following methods:
**Homebrew (macOS/Linux):**
```bash
brew tap holon-run/homebrew-tap
brew install uxc
```
**Install Script (macOS/Linux, review before running):**
```bash
curl -fsSL https://raw.githubusercontent.com/holon-run/uxc/main/scripts/install.sh -o install-uxc.sh
less install-uxc.sh
bash install-uxc.sh
```
**Cargo:**
```bash
cargo install uxc
```
## Core Workflow
1. Use fixed link command by default:
- `command -v deepwiki-mcp-cli`
- If missing, create it: `uxc link deepwiki-mcp-cli mcp.deepwiki.com/mcp`
- `deepwiki-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick a different fixed command name.
2. Ask a question about a repository:
- `deepwiki-mcp-cli ask_question repoName=owner/repo question='your question'`
3. Read wiki structure:
- `deepwiki-mcp-cli read_wiki_structure repoName=owner/repo`
4. Read wiki contents:
- `deepwiki-mcp-cli read_wiki_contents repoName=owner/repo`
## Available Tools
- **ask_question**: Ask any question about a GitHub repository and get an AI-powered response
- **read_wiki_structure**: Get a list of documentation topics for a repository
- **read_wiki_contents**: View documentation about a repository
## Usage Examples
### Ask about a codebase
```bash
deepwiki-mcp-cli ask_question repoName=facebook/react question='How does useState work?'
```
### Explore repository structure
```bash
deepwiki-mcp-cli read_wiki_structure '{"repoName":"facebook/react"}'
```
### Read documentation
```bash
deepwiki-mcp-cli read_wiki_contents repoName=facebook/react
```
## Output Parsing
The response is an MCP JSON envelope. Extract the content from `.data.content[].text`.
## Notes
- Maximum 10 repositories per question
- Some popular repositories may already be indexed
- Responses are grounded in the actual codebase
- `deepwiki-mcp-cli <operation> ...` is equivalent to `uxc mcp.deepwiki.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use direct `uxc mcp.deepwiki.com/mcp ...` calls as fallback.
## Reference Files
- Workflow details: `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "DeepWiki"
short_description: "Query GitHub repo docs via DeepWiki MCP"
default_prompt: "Use $deepwiki-mcp-skill to ask questions about GitHub repositories and read their documentation via DeepWiki MCP."
FILE:references/usage-patterns.md
# DeepWiki Usage Patterns
This skill defaults to fixed link command `deepwiki-mcp-cli`.
Create it when missing:
```bash
command -v deepwiki-mcp-cli
uxc link deepwiki-mcp-cli mcp.deepwiki.com/mcp
```
## Basic Question Flow
1. Ensure the repository is indexed on DeepWiki (visit https://deepwiki.com)
2. Ask a question using `ask_question` tool:
```bash
deepwiki-mcp-cli ask_question repoName=owner/repo question='your question'
```
## Common Use Cases
### Understand a function or API
```bash
deepwiki-mcp-cli ask_question repoName=facebook/react question='How does useState work?'
```
### Find relevant code
```bash
deepwiki-mcp-cli ask_question '{"repoName":"owner/repo","question":"Where is the authentication logic?"}'
```
### Get code review context
```bash
deepwiki-mcp-cli ask_question repoName=owner/repo question='Explain the architecture of this project'
```
## Output Handling
Parse the response:
```bash
# Extract the answer text
deepwiki-mcp-cli ask_question repoName=facebook/react question='What is React?' | jq -r '.data.content[].text'
```
## Fallback Equivalence
- `deepwiki-mcp-cli <operation> ...` is equivalent to `uxc mcp.deepwiki.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use `uxc mcp.deepwiki.com/mcp ...` as fallback.
## Limitations
- Repository must be indexed first
- Max 10 repositories per question
- Some repositories may not be indexed yet
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/deepwiki-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
# Check dependencies
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
if [[ ! -f "file" ]]; then
fail "missing required file: file"
fi
done
# Validate SKILL frontmatter minimum fields.
# Require the first line to be '---' and a subsequent closing '---'.
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*deepwiki-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: deepwiki-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
# Validate required invocation contract appears in SKILL text.
if ! rg -q 'mcp.deepwiki.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must document MCP endpoint"
fi
if ! rg -q 'ask_question' "SKILL_FILE"; then
fail "SKILL.md must document ask_question tool"
fi
if ! rg -q 'command -v deepwiki-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link deepwiki-mcp-cli mcp.deepwiki.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'deepwiki-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use deepwiki-mcp-cli help-first discovery"
fi
if ! rg -q 'ask_question repoName=' "SKILL_FILE"; then
fail "SKILL.md must prefer key=value examples for ask_question"
fi
if ! rg -q "read_wiki_structure .*'\\{.*\\}'" "SKILL_FILE"; then
fail "SKILL.md must include a bare JSON positional example"
fi
if rg -q -- '--input-json|deepwiki-mcp-cli list|deepwiki-mcp-cli describe|deepwiki-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "deepwiki docs must not use list/describe/call/--input-json in default examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "deepwiki docs must not pass raw JSON via --args"
fi
# Validate references linked from SKILL body.
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc mcp.deepwiki.com/mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "deepwiki docs must include single-point fallback equivalence guidance"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "deepwiki docs must not include dynamic command renaming guidance"
fi
# Validate openai.yaml minimum fields.
if ! rg -q '^\s*display_name:\s*"DeepWiki"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$deepwiki-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $deepwiki-mcp-skill'
fi
echo "skills/deepwiki-mcp-skill validation passed"
Query up-to-date library documentation and code examples using Context7 MCP. Use when you need current, version-specific documentation for npm packages, Pyth...
---
name: context7-mcp-skill
description: Query up-to-date library documentation and code examples using Context7 MCP. Use when you need current, version-specific documentation for npm packages, Python libraries, or other programming languages.
metadata:
short-description: Query library docs via Context7 MCP
---
# Context7 Skill
Use this skill to query library documentation and code examples.
## Prerequisites
- `uxc` skill is installed (see [uxc skill](https://github.com/holon-run/uxc/tree/main/skills/uxc) for installation)
- Network access to `https://mcp.context7.com/mcp`
## Core Workflow
1. Use fixed link command by default:
- `command -v context7-mcp-cli`
- If missing, create it: `uxc link context7-mcp-cli mcp.context7.com/mcp`
- `context7-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick a different fixed command name.
2. Resolve a library name to get library ID:
- `context7-mcp-cli resolve-library-id libraryName=react query='useState hook'`
3. Query documentation:
- `context7-mcp-cli query-docs libraryId=/reactjs/react.dev query='how to use useState'`
## Available Tools
- **resolve-library-id**: Resolve a package/library name to Context7 library ID
- **query-docs**: Query documentation and code examples for a specific library
## Usage Examples
### Find React documentation
```bash
# First resolve the library
context7-mcp-cli resolve-library-id libraryName=react query='React useState hook'
```
### Query specific documentation
```bash
context7-mcp-cli query-docs '{"libraryId":"/reactjs/react.dev","query":"how to use useEffect"}'
```
### Query Node.js documentation
```bash
context7-mcp-cli resolve-library-id libraryName=node query='file system'
```
## Notes
- Requires library name first, then use the returned libraryId for queries
- Context7 provides version-specific, up-to-date documentation
- Supports npm packages, Python libraries, and more
- `context7-mcp-cli <operation> ...` is equivalent to `uxc mcp.context7.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use direct `uxc mcp.context7.com/mcp ...` calls as fallback.
## Reference Files
- Workflow details: `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Context7"
short_description: "Query library docs via Context7 MCP"
default_prompt: "Use $context7-mcp-skill to query up-to-date library documentation and code examples via Context7 MCP."
FILE:references/usage-patterns.md
# Context7 Usage Patterns
This skill defaults to fixed link command `context7-mcp-cli`.
Create it when missing:
```bash
command -v context7-mcp-cli
uxc link context7-mcp-cli mcp.context7.com/mcp
```
## Basic Query Flow
1. First resolve a library name to get library ID:
```bash
context7-mcp-cli resolve-library-id libraryName=package-name query='what you need'
```
2. Then use the returned libraryId to query documentation:
```bash
context7-mcp-cli query-docs libraryId=/org/project query='your question'
```
## Common Use Cases
### Find React hooks documentation
```bash
context7-mcp-cli resolve-library-id libraryName=react query='useState hook'
```
### Query specific API
```bash
context7-mcp-cli query-docs '{"libraryId":"/reactjs/react.dev","query":"how to use useEffect"}'
```
### Find Node.js fs module docs
```bash
context7-mcp-cli resolve-library-id libraryName=node query='file system'
```
## Output Handling
Parse the response:
```bash
# Extract the answer text
context7-mcp-cli query-docs libraryId=/reactjs/react.dev query=useState | jq -r '.data.content[].text'
```
## Fallback Equivalence
- `context7-mcp-cli <operation> ...` is equivalent to `uxc mcp.context7.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use `uxc mcp.context7.com/mcp ...` as fallback.
## Limitations
- Must resolve library first before querying
- Some libraries may have multiple matches - choose the best one
- Context7 provides up-to-date docs, but coverage varies by library
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/context7-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
# Check dependencies
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
if [[ ! -f "file" ]]; then
fail "missing required file: file"
fi
done
# Validate SKILL frontmatter minimum fields.
# Require the first line to be '---' and a subsequent closing '---'.
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*context7-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: context7-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
# Validate required invocation contract appears in SKILL text.
if ! rg -q 'mcp.context7.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must document MCP endpoint"
fi
if ! rg -q 'resolve-library-id' "SKILL_FILE"; then
fail "SKILL.md must document resolve-library-id tool"
fi
if ! rg -q 'query-docs' "SKILL_FILE"; then
fail "SKILL.md must document query-docs tool"
fi
if ! rg -q 'command -v context7-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link context7-mcp-cli mcp.context7.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'context7-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use context7-mcp-cli help-first discovery"
fi
# Validate preferred input style appears in SKILL text.
if ! rg -q "resolve-library-id libraryName=" "SKILL_FILE"; then
fail "SKILL.md must prefer key=value examples for resolve-library-id"
fi
if ! rg -q "query-docs .*'\\{.*\\}'" "SKILL_FILE"; then
fail "SKILL.md must include a bare JSON positional example"
fi
if rg -q -- '--input-json|context7-mcp-cli list|context7-mcp-cli describe|context7-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not use list/describe/call/--input-json in default examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not pass raw JSON via --args"
fi
# Validate references linked from SKILL body.
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc mcp.context7.com/mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must include single-point fallback equivalence guidance"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not include dynamic command renaming guidance"
fi
# Validate openai.yaml minimum fields.
if ! rg -q '^\s*display_name:\s*"Context7"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$context7-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $context7-mcp-skill'
fi
echo "skills/context7-mcp-skill validation passed"
Query up-to-date library documentation and code examples using Context7 MCP. Use when you need current, version-specific documentation for npm packages, Pyth...
---
name: context7
description: Query up-to-date library documentation and code examples using Context7 MCP. Use when you need current, version-specific documentation for npm packages, Python libraries, or other programming languages.
metadata:
short-description: Query library docs via Context7 MCP
---
# Context7 Skill
Use this skill to query library documentation and code examples.
## Prerequisites
- `uxc` skill is installed (see [uxc skill](https://github.com/holon-run/uxc/tree/main/skills/uxc) for installation)
- Network access to `https://mcp.context7.com/mcp`
## Core Workflow
1. Use fixed link command by default:
- `command -v context7-mcp-cli`
- If missing, create it: `uxc link context7-mcp-cli mcp.context7.com/mcp`
- `context7-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick a different fixed command name.
2. Resolve a library name to get library ID:
- `context7-mcp-cli resolve-library-id libraryName=react query='useState hook'`
3. Query documentation:
- `context7-mcp-cli query-docs libraryId=/reactjs/react.dev query='how to use useState'`
## Available Tools
- **resolve-library-id**: Resolve a package/library name to Context7 library ID
- **query-docs**: Query documentation and code examples for a specific library
## Usage Examples
### Find React documentation
```bash
# First resolve the library
context7-mcp-cli resolve-library-id libraryName=react query='React useState hook'
```
### Query specific documentation
```bash
context7-mcp-cli query-docs '{"libraryId":"/reactjs/react.dev","query":"how to use useEffect"}'
```
### Query Node.js documentation
```bash
context7-mcp-cli resolve-library-id libraryName=node query='file system'
```
## Notes
- Requires library name first, then use the returned libraryId for queries
- Context7 provides version-specific, up-to-date documentation
- Supports npm packages, Python libraries, and more
- `context7-mcp-cli <operation> ...` is equivalent to `uxc mcp.context7.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use direct `uxc mcp.context7.com/mcp ...` calls as fallback.
## Reference Files
- Workflow details: `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Context7"
short_description: "Query library docs via Context7 MCP"
default_prompt: "Use $context7 to query up-to-date library documentation and code examples via Context7 MCP."
FILE:references/usage-patterns.md
# Context7 Usage Patterns
This skill defaults to fixed link command `context7-mcp-cli`.
Create it when missing:
```bash
command -v context7-mcp-cli
uxc link context7-mcp-cli mcp.context7.com/mcp
```
## Basic Query Flow
1. First resolve a library name to get library ID:
```bash
context7-mcp-cli resolve-library-id libraryName=package-name query='what you need'
```
2. Then use the returned libraryId to query documentation:
```bash
context7-mcp-cli query-docs libraryId=/org/project query='your question'
```
## Common Use Cases
### Find React hooks documentation
```bash
context7-mcp-cli resolve-library-id libraryName=react query='useState hook'
```
### Query specific API
```bash
context7-mcp-cli query-docs '{"libraryId":"/reactjs/react.dev","query":"how to use useEffect"}'
```
### Find Node.js fs module docs
```bash
context7-mcp-cli resolve-library-id libraryName=node query='file system'
```
## Output Handling
Parse the response:
```bash
# Extract the answer text
context7-mcp-cli query-docs libraryId=/reactjs/react.dev query=useState | jq -r '.data.content[].text'
```
## Fallback Equivalence
- `context7-mcp-cli <operation> ...` is equivalent to `uxc mcp.context7.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use `uxc mcp.context7.com/mcp ...` as fallback.
## Limitations
- Must resolve library first before querying
- Some libraries may have multiple matches - choose the best one
- Context7 provides up-to-date docs, but coverage varies by library
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/context7"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
# Check dependencies
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
if [[ ! -f "file" ]]; then
fail "missing required file: file"
fi
done
# Validate SKILL frontmatter minimum fields.
# Require the first line to be '---' and a subsequent closing '---'.
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*context7\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: context7"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
# Validate required invocation contract appears in SKILL text.
if ! rg -q 'mcp.context7.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must document MCP endpoint"
fi
if ! rg -q 'resolve-library-id' "SKILL_FILE"; then
fail "SKILL.md must document resolve-library-id tool"
fi
if ! rg -q 'query-docs' "SKILL_FILE"; then
fail "SKILL.md must document query-docs tool"
fi
if ! rg -q 'command -v context7-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link context7-mcp-cli mcp.context7.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'context7-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use context7-mcp-cli help-first discovery"
fi
# Validate preferred input style appears in SKILL text.
if ! rg -q "resolve-library-id libraryName=" "SKILL_FILE"; then
fail "SKILL.md must prefer key=value examples for resolve-library-id"
fi
if ! rg -q "query-docs .*'\\{.*\\}'" "SKILL_FILE"; then
fail "SKILL.md must include a bare JSON positional example"
fi
if rg -q -- '--input-json|context7-mcp-cli list|context7-mcp-cli describe|context7-mcp-cli call' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not use list/describe/call/--input-json in default examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not pass raw JSON via --args"
fi
# Validate references linked from SKILL body.
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc mcp.context7.com/mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must include single-point fallback equivalence guidance"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "context7 docs must not include dynamic command renaming guidance"
fi
# Validate openai.yaml minimum fields.
if ! rg -q '^\s*display_name:\s*"Context7"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$context7.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $context7'
fi
echo "skills/context7 validation passed"
Create wrapper skills that call remote tools through UXC. Use when defining a new provider skill and you need reusable templates, validation rules, and anti-...
---
name: uxc-skill-creator
description: Create wrapper skills that call remote tools through UXC. Use when defining a new provider skill and you need reusable templates, validation rules, and anti-pattern guidance based on proven UXC skill practices.
---
# UXC Skill Creator
Use this skill to design and standardize provider wrapper skills built on top of `uxc`.
## Prerequisites
- `uxc` skill is available as the base execution contract.
- Target wrapper skill scope is clear (provider endpoint, core operations, auth model, write risk).
## Output Contract
A wrapper skill created with this skill should include:
- `SKILL.md`
- `agents/openai.yaml`
- `references/usage-patterns.md`
- `scripts/validate.sh`
Optional files are allowed only when they add real reusable value.
## Core Workflow
1. Start from user-provided host input:
- record the raw host the user gives
- normalize endpoint candidates (scheme/no-scheme, path variants)
2. Discover protocol and valid path before drafting skill text:
- search official docs/repo to confirm endpoint shape and auth model
- probe candidates with `uxc <endpoint> -h`
- confirm one working endpoint + protocol as the wrapper target
3. Detect authentication requirement explicitly:
- run host help or a minimal read call and inspect envelope/error code
- if auth-protected, record required model (api key or oauth) and scopes
- verify local mapping path with `uxc auth binding match <endpoint>` when OAuth/binding is used
4. Fix the wrapper interface:
- provider endpoint (`<host>`)
- fixed link command name (`<provider>-<protocol>-cli`)
- auth mode (none, api key, oauth)
5. Write `SKILL.md` as a thin execution policy:
- link-first command flow
- help-first discovery flow
- JSON envelope parsing and safe-write guardrails
6. Add provider-specific `references/usage-patterns.md`:
- minimal read and write examples
- key=value and bare JSON positional input examples
7. Add `scripts/validate.sh` with strict checks:
- required files
- frontmatter fields
- command style constraints
- banned legacy patterns
8. Add `agents/openai.yaml` for skill UI metadata.
9. Run validation and iterate until clean.
## Hard Rules
- Default to link-first (`command -v <link_name>` then `uxc link <link_name> <host>`).
- Default to help-first (`<link_name> -h`, `<link_name> <operation> -h`).
- Use protocol-aware link naming:
- format: `<provider>-<protocol>-cli`
- examples: `notion-mcp-cli`, `github-openapi-cli`
- Prefer `key=value`; allow bare JSON positional payload.
- Keep JSON output as automation path; do not rely on `--text`.
- Do not use legacy default examples (`list`/`describe`/`call`/removed flags).
- Do not use dynamic link renaming at runtime.
- Do not assume protocol/path/auth from host string alone; verify by search + probe.
## References
- Step-by-step implementation flow:
- `references/workflow.md`
- Copy-ready templates:
- `references/templates.md`
- Validation checklist and banned patterns:
- `references/validation-rules.md`
- Observed pitfalls and better defaults:
- `references/anti-patterns.md`
## See Also
- Base execution and protocol/auth guidance:
- `skills/uxc/SKILL.md`
FILE:agents/openai.yaml
interface:
display_name: "UXC Skill Creator"
short_description: "Create consistent UXC-based wrapper skills"
default_prompt: "Use $uxc-skill-creator to create or refine UXC-based wrapper skills with link-first, help-first, and strict validation conventions."
FILE:references/anti-patterns.md
# Anti-Patterns
## 1. Probe-first auth by remote call
Avoid adding extra remote probe calls just to test auth readiness.
Use local binding checks first, then validate through the first real operation call.
## 2. Dynamic link renaming at runtime
Do not teach runtime logic to append suffixes or auto-rename command names.
Link naming is a skill author decision at design time.
## 3. Overweight wrapper skill
Do not duplicate generic OAuth/error docs in each provider skill.
Keep wrappers thin and reference `skills/uxc` for shared behavior.
## 4. Ambiguous invocation style
Avoid mixed, obsolete, or conflicting argument styles in examples.
Prefer `key=value`, and include bare JSON positional only where it adds clarity.
## 5. Write-before-read flows
Do not default to write operations.
Read current state first, then require explicit user confirmation before destructive or high-impact writes.
## 6. Host-only assumptions
Do not infer protocol/path/auth only from host shape.
Always verify with official docs search and `uxc` probing before publishing wrapper commands.
FILE:references/templates.md
# Templates
## Minimal Wrapper SKILL.md Template
````markdown
---
name: <skill-name>
description: <what it does + when to use>
---
# <Skill Title>
Use this skill to run <provider> operations through `uxc`.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `<host>`.
## Core Workflow
1. Confirm endpoint/protocol/auth from user host:
- search official docs for canonical endpoint and auth requirements
- probe with `uxc <host> -h` (and endpoint variants if needed)
2. Use fixed link command by default:
- `command -v <link_name>`
- If missing, create it: `uxc link <link_name> <host>`
- `<link_name> -h`
3. Inspect operation schema:
- `<link_name> <operation> -h`
4. Execute operation:
- `<link_name> <operation> field=value`
- `<link_name> <operation> '{"field":"value"}'`
## Guardrails
- Parse JSON envelope fields (`ok`, `data`, `error`).
- Require explicit user confirmation for destructive writes.
- `<link_name> <operation> ...` is equivalent to `uxc <host> <operation> ...`.
- When OAuth/binding is used, include local mapping check:
- `uxc auth binding match <endpoint>`
## References
- `references/usage-patterns.md`
````
Link naming convention to apply in templates:
- `<provider>-<protocol>-cli`
- keep the name fixed at skill-authoring time
- do not auto-rename at runtime
## Minimal usage-patterns.md Template
````markdown
# Usage Patterns
```bash
command -v <link_name>
uxc link <link_name> <host>
<link_name> -h
```
## Read path
```bash
<link_name> <read_operation> field=value
```
## Bare JSON positional example
```bash
<link_name> <operation> '{"field":"value"}'
```
## Fallback equivalence
- `<link_name> <operation> ...` is equivalent to `uxc <host> <operation> ...`.
````
Replace placeholders before use:
- `<skill-name>` -> actual folder name
- `<link_name>` -> fixed command name
- `<host>` -> verified endpoint
- `<operation>` / `<read_operation>` -> real operation IDs
## Minimal validate.sh Template
```bash
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/<skill-name>"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() { printf '[validate] error: %s\n' "$*" >&2; exit 1; }
need_cmd() { command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"; }
need_cmd rg
for f in "SKILL_FILE" "OPENAI_FILE" "SKILL_DIR/references/usage-patterns.md"; do
[[ -f "$f" ]] || fail "missing required file: $f"
done
rg -q '^name:\s*<skill-name>\s*$' "SKILL_FILE" || fail 'invalid skill name'
rg -q '^description:\s*.+' "SKILL_FILE" || fail 'missing description'
rg -q 'command -v <link_name>' "SKILL_FILE" || fail 'missing link-first check'
rg -q '<link_name> -h' "SKILL_FILE" || fail 'missing help-first usage'
if rg -q -- '(^|[[:space:]])uxc <host> (list|describe|call)([[:space:]]|$)|(^|[[:space:]])<link_name> (list|describe|call)([[:space:]]|$)|--input-json|--args .*\{' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail 'found banned legacy patterns'
fi
echo "skills/<skill-name> validation passed"
```
FILE:references/validation-rules.md
# Validation Rules
## Required checks
1. Frontmatter exists and includes only required routing fields:
- `name`
- `description`
2. Required files exist:
- `SKILL.md`
- `agents/openai.yaml`
- `references/usage-patterns.md`
- `scripts/validate.sh`
3. Link-first pattern is explicit:
- `command -v <link_name>`
- `uxc link <link_name> <host>`
4. Help-first pattern is explicit:
- `<link_name> -h`
- `<link_name> <operation> -h`
5. Execution input style is explicit:
- at least one `key=value` example
- at least one bare JSON positional example
6. Link naming convention is explicit:
- use `<provider>-<protocol>-cli`
- keep the name fixed for the skill
7. Discovery workflow is explicit:
- start from user-provided host
- include search + `uxc <endpoint> -h` probe before finalizing protocol/path
8. Auth detection workflow is explicit:
- include how auth requirement is determined from probe/first call errors
- include binding verification when OAuth/binding is used
## Banned defaults
Reject these in default examples and workflow text:
- `list`/`describe`/`call` old command framing
- removed input flags or deprecated invocation forms
- raw JSON passed through `--args`
- dynamic runtime command renaming for link conflicts
- protocol/path/auth assumptions made without verification
## OAuth and error handling boundary
For wrapper skills, keep provider-specific notes minimal and reuse `skills/uxc` for canonical OAuth and error playbooks.
FILE:references/workflow.md
# Workflow
## 1. Intake host and normalize candidates
Start from the endpoint/host provided by the user.
Build a candidate list before committing to one:
- raw value from user
- shorthand host/path form
- full URL form (`https://...`)
- common provider path variants (for MCP often `/mcp`)
Keep the candidate list explicit in working notes.
## 2. Discover protocol and path (search + probe)
Use both external signals and runtime probing:
1. Search official docs or repository README for:
- protocol type (MCP, OpenAPI, GraphQL, gRPC, JSON-RPC)
- canonical endpoint path
- authentication requirements
2. Probe each endpoint candidate:
- `uxc <candidate_endpoint> -h`
3. Pick the first endpoint that gives valid operation help and stable behavior.
Do not finalize wrapper naming or examples until this step is complete.
## 3. Detect auth requirements
Lock these values before writing examples:
- Skill name (folder-safe, stable)
- Provider endpoint host/path (verified)
- Fixed link command name (`<provider>-<protocol>-cli`)
- Auth model and required scopes
- High-risk operations that require explicit user confirmation
Use protocol-aware names for cross-protocol consistency:
- MCP wrapper: `notion-mcp-cli`
- OpenAPI wrapper: `github-openapi-cli`
- GraphQL wrapper: `shopify-graphql-cli`
For OAuth/binding flows, verify local state mapping with:
```bash
uxc auth binding match <endpoint>
```
Use first real read call as runtime auth validation.
## 4. Draft wrapper `SKILL.md`
Keep it thin and operational:
- one-sentence purpose
- prerequisites
- core workflow commands
- guardrails
- references list
Delegate generic auth/error patterns to `skills/uxc` references.
## 5. Draft `references/usage-patterns.md`
Include only real recurring calls:
- bootstrap (`command -v`, `uxc link`, `<link_name> -h`)
- read path examples
- write path examples (if provider supports write)
- output parsing notes based on envelope fields
## 6. Add `agents/openai.yaml`
Provide concise UI metadata:
- `display_name`
- `short_description`
- `default_prompt`
## 7. Implement `scripts/validate.sh`
Codify non-negotiables as checks instead of prose:
- required files exist
- frontmatter has `name` and `description`
- link-first and help-first commands exist
- old patterns are rejected
- host/protocol/auth discovery workflow exists
## 8. Validate and iterate
Run:
```bash
bash skills/<skill-name>/scripts/validate.sh
```
Fix failures until validation is clean.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/uxc-skill-creator"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/workflow.md"
"SKILL_DIR/references/templates.md"
"SKILL_DIR/references/validation-rules.md"
"SKILL_DIR/references/anti-patterns.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*uxc-skill-creator\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: uxc-skill-creator"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'link-first' "SKILL_FILE"; then
fail "SKILL.md must document link-first convention"
fi
if ! rg -q 'help-first' "SKILL_FILE"; then
fail "SKILL.md must document help-first convention"
fi
if ! rg -q '<provider>-<protocol>-cli' "SKILL_FILE"; then
fail "SKILL.md must document protocol-aware link naming convention"
fi
if ! rg -q 'Start from user-provided host input' "SKILL_FILE"; then
fail "SKILL.md must document host-driven workflow intake"
fi
if ! rg -q 'search official docs/repo' "SKILL_FILE"; then
fail "SKILL.md must document external search for protocol/path/auth discovery"
fi
if ! rg -q 'probe candidates with `uxc <endpoint> -h`' "SKILL_FILE"; then
fail "SKILL.md must document uxc probe-based endpoint validation"
fi
if ! rg -q 'uxc auth binding match <endpoint>' "SKILL_FILE"; then
fail "SKILL.md must document local binding verification for oauth/binding flows"
fi
if ! rg -q 'references/workflow.md' "SKILL_FILE"; then
fail "SKILL.md must reference workflow.md"
fi
if ! rg -q 'references/templates.md' "SKILL_FILE"; then
fail "SKILL.md must reference templates.md"
fi
if ! rg -q 'references/validation-rules.md' "SKILL_FILE"; then
fail "SKILL.md must reference validation-rules.md"
fi
if ! rg -q 'references/anti-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference anti-patterns.md"
fi
if rg -q -- '(^|[[:space:]])uxc <host> (list|describe|call)([[:space:]]|$)|(^|[[:space:]])<link_name> (list|describe|call)([[:space:]]|$)' "SKILL_FILE" "SKILL_DIR/references/"*.md; then
fail "uxc-skill-creator docs must not use deprecated list/describe/call invocation forms"
fi
if ! rg -q '^\s*display_name:\s*"UXC Skill Creator"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$uxc-skill-creator.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $uxc-skill-creator'
fi
echo "skills/uxc-skill-creator validation passed"
Discover and call remote schema-exposed interfaces with UXC. Use when an agent or skill needs to list operations, inspect operation schemas, and execute Open...
---
name: uxc
description: Discover and call remote schema-exposed interfaces with UXC. Use when an agent or skill needs to list operations, inspect operation schemas, and execute OpenAPI, GraphQL, gRPC, MCP, or JSON-RPC calls via one CLI contract.
metadata:
short-description: Discover and call remote schema APIs via UXC
---
# UXC Skill
Use this skill when a task requires calling a remote interface and the endpoint can expose machine-readable schema metadata.
## When To Use
- You need to call APIs/tools from another skill and want one consistent CLI workflow.
- The interface may be OpenAPI, GraphQL, gRPC reflection, MCP, or JSON-RPC/OpenRPC.
- You need deterministic, machine-readable output (`ok`, `kind`, `data`, `error`).
Do not use this skill for pure local file operations with no remote interface.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- For gRPC runtime calls, `grpcurl` is installed and available in `PATH`.
### Install uxc
Choose one of the following methods:
**Homebrew (macOS/Linux):**
```bash
brew tap holon-run/homebrew-tap
brew install uxc
```
**Install Script (macOS/Linux, review before running):**
```bash
curl -fsSL https://raw.githubusercontent.com/holon-run/uxc/main/scripts/install.sh -o install-uxc.sh
# Review the script before running it
less install-uxc.sh
bash install-uxc.sh
```
**Cargo:**
```bash
cargo install uxc
```
For more options, see the [Installation](https://github.com/holon-run/uxc#installation) section in the UXC README.
## Core Workflow
1. Discover operations:
- `uxc <host> -h`
2. Inspect a specific operation:
- `uxc <host> <operation> -h`
3. Execute with structured input:
- `uxc <host> <operation> key=value`
- `uxc <host> <operation> '<payload-json>'`
4. Parse result as JSON envelope:
- Success: `.ok == true`, consume `.data`
- Failure: `.ok == false`, inspect `.error.code` and `.error.message`
5. For disambiguation, use operation-level help first:
- `uxc <host> <operation> -h`
6. For auth-protected endpoints, use the right auth track:
- simple bearer / single-secret API key: see `references/auth-configuration.md`
- multi-field auth or request signing: see `references/auth-configuration.md`
- OAuth flows: see `references/oauth-and-binding.md`
## Link-First Workflow For Wrapper Skills
Wrapper skills should default to a fixed local link command instead of calling `uxc <host> ...` directly on every step.
1. Pick a fixed command name during skill development:
- naming convention: `<provider>-mcp-cli`
- examples: `notion-mcp-cli`, `context7-mcp-cli`, `deepwiki-mcp-cli`
2. Check whether the command already exists:
- `command -v <link_name>`
3. If command is missing, create it:
- `uxc link <link_name> <host>`
- For OpenAPI services whose schema is hosted at a separate fixed URL, create the link with `uxc link <link_name> <host> --schema-url <schema_url>`
- For stdio hosts that need credential-driven child env auth, create the link with `uxc link <link_name> <host> --credential <credential_id> --inject-env NAME={{secret}}`
4. Validate link command:
- `<link_name> -h`
5. Use only the link command for the rest of the skill flow.
### Naming Governance
- Link naming is a skill author decision, not a runtime agent decision.
- Resolve ecosystem conflicts during skill development/review.
- Do not implement dynamic rename logic inside runtime skill flow.
- If runtime detects a command conflict that cannot be safely reused, stop and ask for skill maintainer intervention.
### Equivalence Rule
- `<link_name> <operation> ...` is equivalent to `uxc <host> <operation> ...`.
- If the link was created with `--schema-url <schema_url>`, it is equivalent to `uxc <host> --schema-url <schema_url> <operation> ...`.
- If the link was created with `--credential <credential_id> --inject-env NAME={{secret}}`, it is equivalent to `uxc --auth <credential_id> --inject-env NAME={{secret}} <host> <operation> ...`.
- Callers can still override that persisted schema by passing `--schema-url <other_url>` explicitly at runtime.
- Use `uxc <host> ...` only as a temporary fallback when link setup is unavailable.
## Input Modes
- Preferred (simple payload): key/value
- `uxc <host> <operation> field=value`
- Bare JSON positional:
- `uxc <host> <operation> '{"field":"value"}'`
Do not pass raw JSON through `--args`; use positional JSON.
## Output Contract For Reuse
Other skills should treat this skill as the interface execution layer and consume only the stable envelope:
- Success fields: `ok`, `kind`, `protocol`, `endpoint`, `operation`, `data`, `meta`
- Failure fields: `ok`, `error.code`, `error.message`, `meta`
Default output is JSON. Do not use `--text` in agent automation paths.
## Reuse Rule For Other Skills
- If a skill needs remote API/tool execution, reuse this skill instead of embedding protocol-specific calling logic.
- Wrapper skills should adopt a fixed link command (`<provider>-mcp-cli`) as the default invocation path.
- Upstream skill inputs should be limited to:
- target host
- operation id/name
- JSON payload
- required fields to extract from `.data`
## Reference Files (Load On Demand)
- Workflow details and progressive invocation patterns:
- `references/usage-patterns.md`
- Protocol operation naming quick reference:
- `references/protocol-cheatsheet.md`
- Public endpoint examples and availability notes:
- `references/public-endpoints.md`
- Authentication configuration (simple `secret`, named `fields`, headers/query params, and request signers):
- `references/auth-configuration.md`
- OAuth and credential/binding lifecycle:
- `references/oauth-and-binding.md`
- Failure handling and retry strategy:
- `references/error-handling.md`
FILE:agents/openai.yaml
interface:
display_name: "UXC"
short_description: "Discover and call remote schema APIs via UXC"
default_prompt: "Use $uxc to discover operations and call remote interfaces with the UXC CLI."
FILE:references/auth-configuration.md
# Authentication Configuration Guide
Use this guide for non-OAuth authentication. The model now has two tracks:
- simple auth: one primary `secret`
- complex auth: multiple named `fields`
Bindings can optionally attach a typed signer for APIs that require request signing.
## The Two Tracks
### Track 1: Simple `secret`
Keep using `--secret`, `--secret-env`, or `--secret-op` when the provider only needs one secret value.
Good fits:
- bearer tokens
- one API key header
- one API key query param
- stdio `--inject-env NAME={{secret}}`
Examples:
```bash
uxc auth credential set deepwiki \
--auth-type bearer \
--secret-env DEEPWIKI_TOKEN
uxc auth credential set okx-market \
--auth-type api_key \
--secret-env OKX_ACCESS_KEY \
--api-key-header OK-ACCESS-KEY
uxc auth credential set flipside \
--auth-type api_key \
--query-param "apiKey={{secret}}" \
--secret-env FLIPSIDE_API_KEY
```
### Track 2: Named `fields`
Use `--field` when one credential needs multiple values.
Good fits:
- `api_key + secret_key`
- `api_key + private_key`
- `api_key + passphrase`
- multiple headers that should draw from different values
- signer-backed APIs
Example:
```bash
uxc auth credential set exchange \
--auth-type api_key \
--field api_key=env:EXCHANGE_API_KEY \
--field secret_key=env:EXCHANGE_SECRET_KEY
```
## Credential Sources And Templates
### Primary secret sources
`--secret`, `--secret-env`, and `--secret-op` are mutually exclusive.
- literal:
```bash
--secret "actual_secret_value"
```
- env:
```bash
--secret-env CREDENTIAL_NAME
```
- 1Password:
```bash
--secret-op "op://Vault/item/field"
```
### Named field sources
`--field` is repeatable. Each field uses an explicit source form:
```bash
--field api_key=literal:abc123
--field api_key=env:BINANCE_API_KEY
--field private_key=op://Trading/binance/private_key
```
`--field` is not supported for OAuth credentials.
### Template syntax
- `{{secret}}`: primary secret source
- `{{field:<name>}}`: named field on the credential
- `{{env:VAR_NAME}}`: direct environment variable lookup
- `{{op://...}}`: direct 1Password lookup
Examples:
```bash
uxc auth credential set linear \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret-env LINEAR_API_KEY
uxc auth credential set complex-api \
--auth-type api_key \
--field api_key=env:API_KEY \
--field api_secret=env:API_SECRET \
--header "X-API-Key:{{field:api_key}}" \
--header "X-API-Secret:{{field:api_secret}}"
```
## Common Credential Patterns
### Bearer token
```bash
uxc auth credential set myapi \
--auth-type bearer \
--secret-env MYAPI_TOKEN
```
### Raw token in `Authorization`
Use this when the provider rejects `Bearer ` prefix:
```bash
uxc auth credential set linear \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret-env LINEAR_API_KEY
```
### API key in one custom header
```bash
uxc auth credential set custom-api \
--auth-type api_key \
--header "X-API-Key:{{secret}}" \
--secret-env CUSTOM_API_KEY
```
### Multiple headers from different values
```bash
uxc auth credential set okx-advanced \
--auth-type api_key \
--field access_key=env:OKX_ACCESS_KEY \
--field passphrase=env:OKX_PASSPHRASE \
--header "OK-ACCESS-KEY:{{field:access_key}}" \
--header "OK-ACCESS-PASSPHRASE:{{field:passphrase}}"
```
### Query-string API key
```bash
uxc auth credential set flipside \
--auth-type api_key \
--query-param "apiKey={{secret}}" \
--secret-env FLIPSIDE_API_KEY
```
## Bindings
Credentials do nothing until they are bound to endpoint patterns:
```bash
uxc auth binding add \
--id <binding_id> \
--host <api_host> \
--path-prefix <path_prefix> \
--scheme https \
--credential <credential_id> \
--priority 100
```
Resolution order:
1. explicit `--auth <credential_id>`
2. best binding match by `scheme + host + path_prefix + priority`
3. first match wins if all else is equal
Check the effective match:
```bash
uxc auth binding match <endpoint>
```
## Request Signers
Some HTTP APIs need more than static headers. In those cases:
- store values on the credential as `fields`
- attach the signer on the binding with `--signer-json`
Current typed signer kinds:
- `hmac_query_v1`
- `ed25519_query_v1`
### HMAC signed query example
```bash
uxc auth credential set binance-hmac \
--auth-type api_key \
--field api_key=env:BINANCE_API_KEY \
--field secret_key=env:BINANCE_SECRET_KEY
uxc auth binding add \
--id binance-hmac \
--host api.binance.com \
--path-prefix /api/v3 \
--scheme https \
--credential binance-hmac \
--signer-json '{"kind":"hmac_query_v1","algorithm":"hmac_sha256","signing_field":"secret_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"hex","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
```
### Ed25519 signed query example
```bash
uxc auth credential set binance-ed25519 \
--auth-type api_key \
--field api_key=env:BINANCE_API_KEY \
--field private_key=env:BINANCE_ED25519_PRIVATE_KEY
uxc auth binding add \
--id binance-ed25519 \
--host api.binance.com \
--path-prefix /api/v3 \
--scheme https \
--credential binance-ed25519 \
--signer-json '{"kind":"ed25519_query_v1","algorithm":"ed25519","signing_field":"private_key","key_field":"api_key","key_placement":"header","key_name":"X-MBX-APIKEY","signature_param":"signature","signature_encoding":"base64","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
```
## Troubleshooting
### Error: "Bearer token" prefix rejected
Cause: using `--auth-type bearer` when the provider expects a raw token in `Authorization`.
Use:
```bash
uxc auth credential set myapi \
--auth-type api_key \
--header "Authorization:{{secret}}" \
--secret "token"
```
### Error: Credential not found
```bash
uxc auth credential list
```
### Error: No binding matched
```bash
uxc auth binding list
uxc auth binding match <endpoint>
```
### Error: Environment variable not set
If the credential uses env-backed `secret` or `fields`, export the variable and restart daemon:
```bash
export MY_API_KEY="value"
uxc daemon restart
```
### Error: `-1022` or provider-specific invalid signature
Check all three:
1. the binding matched the intended path
2. the `API key` and signing material come from the same provider key record
3. the signer kind matches the provider contract
Useful checks:
```bash
uxc auth credential info <credential_id>
uxc auth binding match <endpoint>
```
### Error: 1Password CLI not found
Install `op`, ensure it is on the daemon's `PATH`, and authenticate before runtime use.
## Verification Steps
After configuring auth:
1. inspect the credential
```bash
uxc auth credential info <credential_id>
```
2. inspect binding match
```bash
uxc auth binding match <endpoint>
```
3. test a read operation first
```bash
uxc <endpoint> <read_operation>
```
4. optionally force the credential explicitly
```bash
uxc --auth <credential_id> <endpoint> <read_operation>
```
## Best Practices
1. Keep `--secret` for single-secret auth. Do not force everything into `fields`.
2. Use `fields` for multi-value credentials and signer-backed APIs.
3. Attach signers to bindings, not to endpoint URLs or shell wrappers.
4. Prefer environment variables or 1Password over literal values.
5. Test with reads before writes.
6. Restart daemon after environment changes.
## See Also
- OAuth flow: `oauth-and-binding.md`
- Error handling: `error-handling.md`
- Usage patterns: `usage-patterns.md`
FILE:references/error-handling.md
# Error Handling
## Envelope-First Handling
Always parse `ok` first.
- `ok=true`: consume `data`
- `ok=false`: branch by `error.code`
## Common Failure Classes
1. Discovery failure
- Symptoms: `list` fails, protocol not detected, endpoint mismatch
- Actions:
- verify host URL/scheme/port
- run with `RUST_LOG=debug`
- for OpenAPI schema separation, provide `--schema-url` or mapping if needed
2. Operation not found
- Symptoms: operation help or runtime call reports unknown operation
- Actions:
- refresh with `-h` on endpoint
- check exact operation naming convention per protocol
3. Input validation failure
- Symptoms: invalid argument / missing field
- Actions:
- inspect operation schema via `<operation> -h`
- start from minimal required payload
- prefer `key=value` or bare positional JSON for primary calls
4. Runtime transport failure
- Symptoms: timeout, connection reset, TLS error
- Actions:
- retry with bounded attempts
- verify endpoint health with native tooling (`curl`, `grpcurl`)
5. OAuth authentication failure
- Symptoms: `OAUTH_REQUIRED`, `OAUTH_REFRESH_FAILED`, `401 invalid_token`
- Actions:
- verify binding with `uxc auth binding match <endpoint>`
- inspect credential with `uxc auth oauth info <credential_id>`
- run `uxc auth oauth refresh <credential_id>`
- if refresh fails, run login again
6. OAuth scope failure
- Symptoms: `OAUTH_SCOPE_INSUFFICIENT`, HTTP `403`
- Actions:
- login again with broader scopes
- confirm provider/workspace policy grants requested scopes
## OAuth Code Playbooks
`OAUTH_REQUIRED`:
1. verify endpoint/credential mapping with binding match
2. login with the expected credential
3. retry original read operation
`OAUTH_DISCOVERY_FAILED`:
1. check endpoint and network reachability
2. retry login
3. if needed, use explicit provider metadata flags supported by CLI
`OAUTH_TOKEN_EXCHANGE_FAILED`:
1. ensure callback URL/code is complete and unmodified
2. restart login flow
`OAUTH_REFRESH_FAILED`:
1. retry refresh once manually
2. if still failing, re-login (refresh token may be revoked/expired)
`OAUTH_SCOPE_INSUFFICIENT`:
1. login with required scopes
2. rerun original operation
## MCP Probe Note
- For MCP HTTP endpoints, `uxc` may refresh OAuth during protocol probe when probe receives `401`.
- If refresh still fails, expect OAuth-related errors instead of a generic protocol mismatch.
## Retry Guidance
- Retry only idempotent read-like operations by default.
- Suggested backoff: 1s, 2s, 4s (max 3 attempts).
- Do not retry validation errors without payload change.
FILE:references/oauth-and-binding.md
# OAuth And Binding
Use this guide for OAuth-backed endpoints across protocols and providers.
## Credential And Binding Model
- Credential stores auth material (`oauth`, bearer, api key).
- Binding maps endpoint patterns to credential IDs.
- Runtime resolution order:
1. explicit `--auth <credential_id>`
2. binding match by `scheme + host + path_prefix + priority`
## Setup Flow
1. Login. Prefer the two-step flow for agents and any multi-turn workflow:
```bash
uxc auth oauth start <credential_id> \
--endpoint <endpoint> \
--redirect-uri <callback_uri>
```
After the user finishes browser authorization:
```bash
uxc auth oauth complete <credential_id> \
--session-id <session_id> \
--authorization-response '<callback_url_or_code>'
```
Single-process interactive fallback:
```bash
uxc auth oauth login <credential_id> \
--endpoint <endpoint> \
--flow authorization_code
```
2. Bind endpoint to credential:
```bash
uxc auth binding add \
--id <binding_id> \
--host <host> \
--path-prefix <path_prefix> \
--scheme <scheme> \
--credential <credential_id> \
--priority 100
```
3. Verify binding:
```bash
uxc auth binding match <endpoint>
```
`<endpoint>` accepts either:
- shorthand host/path like `mcp.notion.com/mcp`
- full URL like `https://mcp.notion.com/mcp`
## Validation Strategy
- Prefer local precheck with `binding match`.
- Do not add redundant preflight read calls by default.
- Use first real read operation as runtime validation.
## Auto Refresh Behavior
For OAuth credentials, `uxc` may refresh automatically:
1. before request when token is near expiry
2. after a `401` retry path in runtime calls
3. during MCP HTTP probe when endpoint returns `401` and OAuth profile is available
If refresh succeeds, call continues with new token.
## Manual Recovery Commands
```bash
uxc auth oauth info <credential_id>
uxc auth oauth refresh <credential_id>
uxc auth oauth logout <credential_id>
```
Use manual `refresh` when troubleshooting or when auto-refresh cannot recover.
## Multi-Binding Troubleshooting
If auth failures persist:
1. list bindings:
- `uxc auth binding list`
2. confirm current default match:
- `uxc auth binding match <endpoint>`
3. verify candidate credentials explicitly:
- `uxc --auth <credential_id> <endpoint> <same_read_operation> ...`
4. remove only bindings confirmed stale/invalid.
## Common Error Codes
- `OAUTH_REQUIRED`
- `OAUTH_DISCOVERY_FAILED`
- `OAUTH_SESSION_NOT_FOUND`
- `OAUTH_SESSION_EXPIRED`
- `OAUTH_TOKEN_EXCHANGE_FAILED`
- `OAUTH_REFRESH_FAILED`
- `OAUTH_SCOPE_INSUFFICIENT`
See `references/error-handling.md` for full recovery playbooks.
FILE:references/protocol-cheatsheet.md
# Protocol Cheatsheet
UXC operation names follow protocol-native conventions.
## OpenAPI
- Operation id format: `method:/path`
- Example:
- `get:/users/{id}`
- `post:/pet`
## gRPC
- Operation id format: `Service/Method`
- Example:
- `addsvc.Add/Sum`
## GraphQL
- Operation id format: `<operation_type>/<field>`
- Example:
- `query/viewer`
- `mutation/addStar`
- `subscription/onEvent`
## MCP
- Operation id format: tool name
- Example:
- `ask_question`
- `list_directory`
## JSON-RPC (OpenRPC-driven)
- Operation id format: method name
- Example:
- `eth_getBalance`
- `net_version`
## Generic Command Templates
```bash
uxc <host> -h
uxc <host> <operation> -h
uxc <host> <operation> field=value
uxc <host> <operation> '{"field":"value"}'
```
FILE:references/public-endpoints.md
# Public Endpoints
The following endpoints are practical no-key baselines for smoke checks. Availability can change over time.
## OpenAPI
- Endpoint: `petstore3.swagger.io/api/v3`
- Check:
```bash
uxc petstore3.swagger.io/api/v3 -h
uxc petstore3.swagger.io/api/v3 get:/store/inventory
```
## GraphQL
- Endpoint: `countries.trevorblades.com`
- Check:
```bash
uxc countries.trevorblades.com -h
uxc countries.trevorblades.com query/country code=US
```
## gRPC
- Endpoint: `grpcb.in:9000` (plaintext), `grpcb.in:9001` (TLS)
- Prerequisite: `grpcurl` installed
- Check:
```bash
uxc grpcb.in:9000 -h
uxc grpcb.in:9000 addsvc.Add/Sum a=1 b=2
```
## MCP (HTTP)
- Endpoint: `mcp.deepwiki.com/mcp`
- Check:
```bash
uxc mcp.deepwiki.com/mcp -h
uxc mcp.deepwiki.com/mcp ask_question -h
```
## JSON-RPC
- Constraint: UXC JSON-RPC discovery requires `openrpc.json`, `/.well-known/openrpc.json`, or `rpc.discover`.
- Current status: no stable, keyless public endpoint is curated in this repository.
- Recommended baseline for reliable tests:
- use a controlled endpoint that exposes OpenRPC/rpc.discover
- or run local/self-hosted JSON-RPC with OpenRPC enabled
FILE:references/usage-patterns.md
# Usage Patterns
## Progressive Flow (Default)
1. Discover operations:
```bash
uxc <host> -h
```
2. Inspect operation input/output shape:
```bash
uxc <host> <operation> -h
```
3. Execute with minimal valid payload:
```bash
uxc <host> <operation> field=value
```
4. Parse success/failure envelope:
```bash
uxc <host> <operation> field=value | jq '.ok, .kind, .data'
```
## Wrapper Pattern (Link-First)
For provider-specific wrapper skills, use a fixed local link command as the default interface:
```bash
command -v <provider>-mcp-cli
# If missing:
uxc link <provider>-mcp-cli <host>
<provider>-mcp-cli -h
<provider>-mcp-cli <operation> -h
<provider>-mcp-cli <operation> field=value
```
For OpenAPI services whose runtime URL and schema URL differ, persist the schema on the link itself:
```bash
uxc link <provider>-openapi-cli <host> --schema-url <schema_url>
<provider>-openapi-cli -h
```
For stdio hosts that expect credentials in child-process env vars, persist credential selection and env injection on the link:
```bash
uxc auth credential set <credential_id> --secret-env <ENV_NAME>
uxc link <provider>-mcp-cli <stdio_command> --credential <credential_id> --inject-env <ENV_NAME>={{secret}}
<provider>-mcp-cli -h
```
For MCP HTTP or other HTTP endpoints that require API keys in the URL query string, configure the credential with `--query-param` and keep the endpoint itself clean:
```bash
uxc auth credential set flipside --auth-type api_key --query-param "apiKey={{secret}}" --secret-env FLIPSIDE_API_KEY
uxc auth binding add --id flipside-mcp --host mcp.flipsidecrypto.xyz --path-prefix /mcp --scheme https --credential flipside --priority 100
uxc https://mcp.flipsidecrypto.xyz/mcp -h
```
For multi-part auth, keep one credential and store extra values as named `fields`:
```bash
uxc auth credential set exchange \
--auth-type api_key \
--field api_key=env:EXCHANGE_API_KEY \
--field secret_key=env:EXCHANGE_SECRET_KEY
```
For signed HTTP APIs, attach the signer to the binding instead of hardcoding signing into the endpoint URL:
```bash
uxc auth binding add \
--id exchange-signed \
--host api.example.com \
--path-prefix /v1 \
--scheme https \
--credential exchange \
--signer-json '{"kind":"hmac_query_v1","algorithm":"hmac_sha256","signing_field":"secret_key","key_field":"api_key","key_placement":"header","key_name":"X-API-KEY","signature_param":"signature","signature_encoding":"hex","timestamp_param":"timestamp","timestamp_unit":"milliseconds","canonicalization":{"mode":"preserve_order"}}' \
--priority 100
```
Examples:
```bash
notion-mcp-cli -h
# Equivalent:
uxc mcp.notion.com/mcp -h
```
```bash
context7-mcp-cli query-docs libraryId=/reactjs/react.dev query=useState
# Equivalent:
uxc mcp.context7.com/mcp query-docs libraryId=/reactjs/react.dev query=useState
```
```bash
discord-openapi-cli get:/gateway
# Equivalent if the link was created with --schema-url:
uxc https://discord.com/api/v10 --schema-url <discord_openapi_spec> get:/gateway
```
### Conflict Handling For Wrapper Skills
- Fixed link command names are decided by skill authors at development time.
- Do not dynamically rename link commands at runtime.
- If a conflicting command name exists and cannot be safely reused, stop and ask a maintainer to update the skill's fixed command name.
## Input Modes
### Bare JSON positional payload
```bash
uxc <host> <operation> '{"field":"value"}'
```
### Key-value arguments
```bash
uxc <host> <operation> field=value
```
Do not pass raw JSON via `--args`; use bare JSON positional payload instead.
## Host-Level Help
```bash
uxc <host> -h
```
Use this when you need quick discovery context before choosing an operation.
## Auth-Protected Flow
1. Confirm mapping:
```bash
uxc auth binding match <endpoint>
```
For signer-backed HTTP auth, also inspect the credential and binding shape before the first live call:
```bash
uxc auth credential info <credential_id>
uxc auth binding list
```
2. Run intended read call directly (use as runtime validation).
3. If auth fails, recover in order:
```bash
uxc auth oauth info <credential_id>
uxc auth oauth refresh <credential_id>
uxc auth oauth start <credential_id> --endpoint <endpoint> --redirect-uri <callback_uri>
uxc auth oauth complete <credential_id> --session-id <session_id> --authorization-response '<callback_url_or_code>'
```
Use `uxc auth oauth login <credential_id> --endpoint <endpoint> --flow authorization_code` only when one interactive process can wait for the callback input.
4. If multiple bindings match, verify explicit credential:
```bash
uxc --auth <credential_id> <endpoint> <operation> '{...}'
```
## Automation Safety Rules
- Keep JSON as default output for machine parsing.
- Treat stderr logs as diagnostic only; parse stdout JSON envelope.
- Start with smallest valid payload before expanding optional fields.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/uxc"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
# Check dependencies
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/references/protocol-cheatsheet.md"
"SKILL_DIR/references/public-endpoints.md"
"SKILL_DIR/references/oauth-and-binding.md"
"SKILL_DIR/references/error-handling.md"
)
for file in "required_files[@]"; do
if [[ ! -f "file" ]]; then
fail "missing required file: file"
fi
done
# Validate SKILL frontmatter minimum fields.
# Require the first line to be '---' and a subsequent closing '---'.
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*uxc\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: uxc"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
# Validate required invocation contract appears in SKILL text.
if ! rg -q 'uxc <host> -h' "SKILL_FILE"; then
fail "SKILL.md must document help-first discovery workflow"
fi
if ! rg -q 'uxc <host> <operation> -h' "SKILL_FILE"; then
fail "SKILL.md must document operation-level help workflow"
fi
if ! rg -q "uxc <host> <operation> key=value" "SKILL_FILE"; then
fail "SKILL.md must document execute workflow"
fi
if ! rg -q "uxc <host> <operation> '<payload-json>'" "SKILL_FILE"; then
fail "SKILL.md must document bare JSON execute workflow"
fi
if rg -q -- 'uxc <host> describe <operation>|uxc <host> call <operation>|--input-json' "SKILL_FILE" "SKILL_DIR/references/"*.md; then
fail "uxc skill docs must not use describe/call/--input-json in default examples"
fi
if ! rg -q 'Link-First Workflow For Wrapper Skills' "SKILL_FILE"; then
fail "SKILL.md must include Link-First workflow guidance for wrapper skills"
fi
if ! rg -q 'naming convention: `<provider>-mcp-cli`' "SKILL_FILE"; then
fail "SKILL.md must define fixed wrapper link naming convention"
fi
if ! rg -q 'command -v <link_name>' "SKILL_FILE"; then
fail "SKILL.md must include link existence check pattern"
fi
if ! rg -q '`<link_name> <operation> ...` is equivalent to `uxc <host> <operation> ...`' "SKILL_FILE"; then
fail "SKILL.md must include link/uxc equivalence rule"
fi
if rg -q "execute notion" "SKILL_FILE"; then
fail "SKILL.md must not document execute-form invocations"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/"*.md; then
fail "uxc docs must not pass raw JSON via --args"
fi
# Validate references linked from SKILL body.
for rel in \
"references/usage-patterns.md" \
"references/protocol-cheatsheet.md" \
"references/public-endpoints.md" \
"references/oauth-and-binding.md" \
"references/error-handling.md"; do
if ! rg -q "rel" "SKILL_FILE"; then
fail "SKILL.md must reference rel"
fi
done
if ! rg -q -F 'Wrapper Pattern (Link-First)' "SKILL_DIR/references/usage-patterns.md"; then
fail "uxc usage-patterns must include wrapper link-first pattern"
fi
if ! rg -q 'Do not dynamically rename link commands at runtime' "SKILL_DIR/references/usage-patterns.md"; then
fail "uxc usage-patterns must forbid dynamic link renaming at runtime"
fi
# Validate openai.yaml minimum fields.
if ! rg -q '^\s*display_name:\s*"UXC"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$uxc.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $uxc'
fi
echo "skills/uxc validation passed"
Operate Notion workspace content through Notion MCP using the UXC CLI, including search, fetch, users/teams lookup, page/database creation and updates, and c...
---
name: notion-mcp-skill
description: Operate Notion workspace content through Notion MCP using the UXC CLI, including search, fetch, users/teams lookup, page/database creation and updates, and comments. Use when tasks require calling Notion tools over MCP with OAuth (authorization_code + PKCE), especially when safe write controls and JSON-envelope parsing are required.
---
# Notion MCP Skill
Use this skill to run Notion MCP operations through `uxc` with OAuth and guarded write behavior.
Reuse the `uxc` skill guidance for discovery, schema inspection, OAuth lifecycle, and error recovery.
Do not assume another skill is auto-triggered in every runtime. Keep this skill executable on its own.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- Network access to `https://mcp.notion.com/mcp`.
- OAuth callback listener is reachable (default examples use `http://127.0.0.1:8788/callback`).
- `uxc` skill is available for generic discovery/describe/execute patterns.
## Core Workflow (Notion-Specific)
Endpoint argument style in this skill:
- Prefer shorthand `mcp.notion.com/mcp` (scheme omitted).
- Full URL `https://mcp.notion.com/mcp` is also valid.
1. Ensure endpoint mapping exists:
- `uxc auth binding match mcp.notion.com/mcp`
2. If mapping/auth is not ready, start OAuth login:
- `uxc auth oauth start notion-mcp --endpoint mcp.notion.com/mcp --redirect-uri http://127.0.0.1:8788/callback --scope read --scope write`
- Prompt user to open the printed authorization URL.
- Ask user to paste the full callback URL after consent.
- Complete login with `uxc auth oauth complete notion-mcp --session-id <session_id> --authorization-response '<callback-url>'`
3. Bind endpoint to the credential:
- `uxc auth binding add --id notion-mcp --host mcp.notion.com --path-prefix /mcp --scheme https --credential notion-mcp --priority 100`
4. Use fixed link command by default:
- `command -v notion-mcp-cli`
- If missing, create it: `uxc link notion-mcp-cli mcp.notion.com/mcp`
- `notion-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick a different fixed command name.
5. Discover tools and inspect schema before execution:
- `notion-mcp-cli -h`
- `notion-mcp-cli notion-fetch -h`
- `notion-fetch` requires `id` (URL or UUID). Examples:
- `notion-mcp-cli notion-fetch id="https://notion.so/your-page-url"`
- `notion-mcp-cli notion-fetch id="12345678-90ab-cdef-1234-567890abcdef"`
- Common operations include `notion-search`, `notion-fetch`, and `notion-update-page`.
6. Prefer read path first:
- Search/fetch current state before any write.
7. Execute writes only after explicit user confirmation:
- For `notion-update-page` operations that may delete content, always confirm intent first.
## OAuth Interaction Template
Use this exact operator-facing flow:
1. Start login command and wait for authorization URL output.
2. Tell user:
- Open this URL in browser and approve Notion access.
- Copy the full callback URL from browser address bar.
- Paste the callback URL back in chat.
3. Run `uxc auth oauth complete notion-mcp --session-id <session_id> --authorization-response '<callback-url>'`.
4. Optionally confirm success with:
- `uxc auth oauth info <credential_id>`
Do not ask user to manually extract or copy bearer tokens. Token exchange is handled by `uxc`.
Use `uxc auth oauth login ... --flow authorization_code` only for a single-process interactive fallback.
## Guardrails
- Keep automation on JSON output envelope; do not use `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `notion-mcp-cli` as the default command path for all Notion MCP calls in this skill.
- `notion-mcp-cli <operation> ...` is equivalent to `uxc mcp.notion.com/mcp <operation> ...`.
- Use direct `uxc mcp.notion.com/mcp ...` only as a temporary fallback when link setup is unavailable.
- Call `notion-fetch` before `notion-create-pages` or `notion-update-page` when targeting database-backed content to obtain exact schema/property names.
- Treat operations as high impact by default:
- Require explicit user confirmation before create/update/move/delete-style actions.
- If OAuth/auth fails, use `uxc` skill OAuth/error playbooks first, then apply Notion-specific checks in this skill's references.
## References
- Notion-specific auth notes (thin wrapper over `uxc` skill OAuth guidance):
- `references/oauth-and-binding.md`
- Invocation patterns by task:
- `references/usage-patterns.md`
- Notion-specific failure notes (thin wrapper over `uxc` skill error guidance):
- `references/error-handling.md`
FILE:agents/openai.yaml
interface:
display_name: "Notion MCP"
short_description: "Operate Notion workspace via UXC + MCP OAuth"
default_prompt: "Use $notion-mcp-skill to search, read, and write Notion content through Notion MCP with UXC, following OAuth and safe-write guardrails."
FILE:references/error-handling.md
# Error Handling
## Scope
This file keeps Notion-specific failure notes only.
For canonical error taxonomy and OAuth recovery playbooks, use `uxc` skill:
- section: `Failure handling and retry strategy`
- file name in `$uxc`: `references/error-handling.md`
## Notion-Specific Failure Notes
1. If first real call returns `invalid_token`:
1. Check for duplicate endpoint bindings (`uxc auth binding list`).
2. Confirm the binding that currently matches:
- `uxc auth binding match mcp.notion.com/mcp`
3. If multiple candidates exist, verify each candidate with explicit credential:
- `uxc --auth <credential_id> mcp.notion.com/mcp <same_read_operation> ...`
4. Remove only the binding(s) confirmed stale/invalid.
5. Retry the original read call that failed.
2. When `notion-update-page` signals deletion risk:
1. Do not retry automatically with permissive flags.
2. Show what would be deleted.
3. Ask for explicit confirmation before executing destructive change.
FILE:references/oauth-and-binding.md
# OAuth And Binding
## Scope
This file keeps Notion-specific OAuth notes only.
For canonical OAuth and binding workflow, use `uxc` skill:
- section: `OAuth and credential/binding lifecycle`
- file name in `$uxc`: `references/oauth-and-binding.md`
## Notion Endpoint Defaults
- endpoint: `mcp.notion.com/mcp`
- endpoint input accepts both `mcp.notion.com/mcp` and `https://mcp.notion.com/mcp`
- suggested scopes: `read`, `write`
- callback example: `http://127.0.0.1:8788/callback`
## Recommended Notion Login
```bash
uxc auth oauth start notion-mcp \
--endpoint mcp.notion.com/mcp \
--redirect-uri http://127.0.0.1:8788/callback \
--scope read \
--scope write
```
After the user approves access, finish with:
```bash
uxc auth oauth complete notion-mcp \
--session-id <session_id> \
--authorization-response 'http://127.0.0.1:8788/callback?code=...&state=...'
```
Notes:
- Omit `--client-id` by default. `uxc` will try dynamic client registration.
- If provider/workspace policy rejects dynamic registration, rerun with explicit `--client-id`.
- `uxc auth oauth login notion-mcp ... --flow authorization_code` remains available as a single-process interactive fallback.
## Interactive Callback Handoff
For agent-driven/manual runs:
1. Run the start command and capture the authorization URL plus `session_id` printed by `uxc`.
2. Ask the user to open the URL and approve access.
3. Ask the user to paste the full callback URL (for example: `http://127.0.0.1:8788/callback?code=...&state=...`).
4. Run `uxc auth oauth complete notion-mcp --session-id <session_id> --authorization-response '<callback-url>'`.
5. Optionally verify with `uxc auth oauth info <credential_id>` when you know the credential id.
## Notion Binding Example
```bash
uxc auth binding add \
--id notion-mcp \
--host mcp.notion.com \
--path-prefix /mcp \
--scheme https \
--credential notion-mcp \
--priority 100
```
Validate match:
```bash
uxc auth binding match mcp.notion.com/mcp
```
## Notion Duplicate-Binding Tip
If multiple bindings match Notion endpoint, verify with explicit credential against the same read call before removing stale bindings.
Do not remove duplicates blindly based on names only.
Default fixed link command for this skill:
```bash
command -v notion-mcp-cli
uxc link notion-mcp-cli mcp.notion.com/mcp
```
If a conflicting command name exists and cannot be safely reused, stop and ask skill maintainers to update the fixed command name.
Then run operation discovery/calls:
```bash
uxc mcp.notion.com/mcp -h
notion-mcp-cli -h
notion-mcp-cli notion-fetch -h
```
FILE:references/usage-patterns.md
# Usage Patterns
All commands assume endpoint `mcp.notion.com/mcp`.
This skill defaults to fixed link command `notion-mcp-cli`.
Create it when missing:
```bash
command -v notion-mcp-cli
uxc link notion-mcp-cli mcp.notion.com/mcp
```
## Discover And Inspect
```bash
notion-mcp-cli -h
notion-mcp-cli notion-fetch -h
```
## Read-First Flows
Search content:
```bash
notion-mcp-cli notion-search query="Q1 plan" query_type=internal
```
Fetch entity by URL/ID:
```bash
notion-mcp-cli notion-fetch id="https://notion.so/your-page-url"
```
List users or teams:
```bash
notion-mcp-cli notion-get-users '{}'
notion-mcp-cli notion-get-teams '{}'
```
## Write Flows (Require Explicit User Confirmation)
Create page:
```bash
notion-mcp-cli notion-create-pages '{
"pages":[
{
"properties":{"title":"Release Notes"},
"content":"# Release Notes\nInitial draft"
}
]
}'
```
Update page properties:
```bash
notion-mcp-cli notion-update-page '{
"page_id":"00000000-0000-0000-0000-000000000000",
"command":"update_properties",
"properties":{"title":"Updated Title"}
}'
```
Add comment:
```bash
notion-mcp-cli notion-create-comment '{
"page_id":"00000000-0000-0000-0000-000000000000",
"rich_text":[{"text":{"content":"Looks good"}}]
}'
```
## Schema Discipline For Database Writes
Before writing to database-backed pages:
1. Fetch database/data source first with `notion-fetch`.
2. Use exact property names from fetched schema.
3. Use expanded formats for date/place fields when required by Notion tool schema.
## Output Parsing
Rely on stable envelope fields:
- Success: `ok == true`, consume `data`
- Failure: `ok == false`, inspect `error.code` and `error.message`
## Fallback Equivalence
- `notion-mcp-cli <operation> ...` is equivalent to `uxc mcp.notion.com/mcp <operation> ...`.
- If link setup is temporarily unavailable, use the `uxc mcp.notion.com/mcp ...` form as a fallback.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/notion-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
"SKILL_DIR/references/oauth-and-binding.md"
"SKILL_DIR/references/error-handling.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*notion-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: notion-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'mcp.notion.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must document Notion MCP endpoint"
fi
if ! rg -q 'notion-search' "SKILL_FILE"; then
fail "SKILL.md must mention notion-search"
fi
if ! rg -q 'notion-fetch' "SKILL_FILE"; then
fail "SKILL.md must mention notion-fetch"
fi
if ! rg -q 'notion-update-page' "SKILL_FILE"; then
fail "SKILL.md must mention notion-update-page"
fi
if ! rg -q 'command -v notion-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link notion-mcp-cli mcp.notion.com/mcp' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'notion-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use notion-mcp-cli help as default discovery path"
fi
if ! rg -q 'notion-mcp-cli notion-fetch -h' "SKILL_FILE"; then
fail "SKILL.md must show operation-level help via notion-mcp-cli <method> -h"
fi
for rel in \
"references/usage-patterns.md" \
"references/oauth-and-binding.md" \
"references/error-handling.md"; do
if ! rg -q "rel" "SKILL_FILE"; then
fail "SKILL.md must reference rel"
fi
done
if ! rg -q '`uxc` skill' "SKILL_FILE"; then
fail "SKILL.md must reference uxc skill guidance for shared OAuth/error handling"
fi
if ! rg -q 'canonical OAuth and binding workflow, use `uxc` skill' "SKILL_DIR/references/oauth-and-binding.md"; then
fail "oauth-and-binding.md must be a thin wrapper pointing to uxc skill guidance"
fi
if ! rg -q 'canonical error taxonomy and OAuth recovery playbooks, use `uxc` skill' "SKILL_DIR/references/error-handling.md"; then
fail "error-handling.md must be a thin wrapper pointing to uxc skill guidance"
fi
if ! rg -q 'equivalent to `uxc mcp.notion.com/mcp' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Notion docs must include single-point fallback equivalence guidance"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "SKILL_DIR/references/"*.md; then
fail "Notion docs must not include dynamic command renaming guidance"
fi
if rg -q ' execute ' "SKILL_FILE"; then
fail "SKILL.md must not include execute-form command examples"
fi
if rg -q -- "--args '\\{" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "Notion skill docs must not pass raw JSON via --args"
fi
if ! rg -q '^\s*display_name:\s*"Notion MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$notion-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $notion-mcp-skill'
fi
echo "skills/notion-mcp-skill validation passed"
Run browser automation through @playwright/mcp over UXC stdio MCP, with daemon-friendly session reuse and safe action guardrails. Use when tasks need determi...
---
name: playwright-mcp-skill
description: Run browser automation through @playwright/mcp over UXC stdio MCP, with daemon-friendly session reuse and safe action guardrails. Use when tasks need deterministic page navigation, DOM snapshots, and scripted browser interaction from CLI.
---
# Playwright MCP Skill
Use this skill to run Playwright MCP operations through `uxc` using fixed stdio endpoints.
Reuse the `uxc` skill for generic protocol discovery, auth/error handling, and envelope parsing rules.
## Prerequisites
- `uxc` is installed and available in `PATH`.
- `npx` is available in `PATH` (Node.js installed).
- Network access for first-time `@playwright/mcp` package fetch.
- Browser runtime can start locally (headless mode is default in this skill).
## Core Workflow (Playwright-Specific)
Endpoint candidate inputs before finalizing:
- Raw package form from docs: `npx @playwright/mcp@latest`
- Reliable non-interactive form: `npx -y @playwright/mcp@latest`
- Isolated/headless stable form (default for this skill):
- `npx -y @playwright/mcp@latest --headless --isolated`
- Shared-profile headless form (for persistent login state):
- `npx -y @playwright/mcp@latest --headless --user-data-dir ~/.uxc/playwright-profile`
- Shared-profile headed form (for interactive debug with same login state):
- `npx -y @playwright/mcp@latest --user-data-dir ~/.uxc/playwright-profile`
1. Verify protocol/path from official source and probe:
- Official source: `https://github.com/microsoft/playwright-mcp`
- probe candidate endpoints with:
- `uxc "npx -y @playwright/mcp@latest --headless --isolated" -h`
- Confirm protocol is MCP stdio (`protocol == "mcp"` in envelope).
2. Detect auth requirement explicitly:
- Run host help or a minimal read call and inspect envelope.
- `@playwright/mcp` default flow is no OAuth/API key for local stdio use.
3. Use fixed link command by default:
- `command -v playwright-mcp-cli`
- If missing, create it:
- `uxc link playwright-mcp-cli "npx -y @playwright/mcp@latest --headless --isolated"`
- Optional shared-profile dual command setup for persistent sessions:
- `command -v playwright-mcp-headless`
- `command -v playwright-mcp-ui`
- `uxc link --daemon-exclusive ~/.uxc/playwright-profile playwright-mcp-headless "npx -y @playwright/mcp@latest --headless --user-data-dir ~/.uxc/playwright-profile"`
- `uxc link --daemon-exclusive ~/.uxc/playwright-profile playwright-mcp-ui "npx -y @playwright/mcp@latest --user-data-dir ~/.uxc/playwright-profile"`
- `playwright-mcp-cli -h`
- If command conflict is detected and cannot be safely reused, stop and ask skill maintainers to pick another fixed command name.
4. Inspect operation schema before execution:
- `playwright-mcp-cli browser_navigate -h`
- `playwright-mcp-cli browser_snapshot -h`
- `playwright-mcp-cli browser_click -h`
5. Prefer read-first interaction:
- Start with navigation/snapshot before mutating page state.
6. Execute actions with explicit confirmation when impact is high:
- Confirm before form submission, checkout, delete/destructive actions, or irreversible multi-step flows.
## Guardrails
- Keep automation on JSON output envelope; do not rely on `--text`.
- Parse stable fields first: `ok`, `kind`, `protocol`, `data`, `error`.
- Use `playwright-mcp-cli` as default command path.
- `playwright-mcp-cli <operation> ...` is equivalent to `uxc "npx -y @playwright/mcp@latest --headless --isolated" <operation> ...`.
- Use direct `uxc "<endpoint>" ...` only as temporary fallback when link setup is unavailable.
- If browser profile conflict appears, keep `--isolated` in endpoint and retry via the same fixed link command.
- When using shared `--user-data-dir`, run headless/headed links serially (not concurrently).
- To enable seamless switching between headed UI login and headless CLI automation using the same profile directory, set a daemon exclusive key:
- Prefer link-level setup (recommended above), or set `UXC_DAEMON_EXCLUSIVE=~/.uxc/playwright-profile` for ad-hoc runs.
- If the profile is still busy (another session is actively running), fallback: `uxc daemon stop`
- Prefer `browser_snapshot` over screenshots for model-action loops.
## References
- Invocation patterns:
- `references/usage-patterns.md`
FILE:agents/openai.yaml
interface:
display_name: "Playwright MCP"
short_description: "Automate browser tasks via @playwright/mcp over UXC stdio"
default_prompt: "Use $playwright-mcp-skill to navigate pages, inspect snapshots, and run browser actions through @playwright/mcp with UXC."
FILE:references/usage-patterns.md
# Usage Patterns
All commands in this skill use the fixed stdio endpoint:
`npx -y @playwright/mcp@latest --headless --isolated`
This skill defaults to fixed link command `playwright-mcp-cli`.
Create it when missing:
```bash
command -v playwright-mcp-cli
uxc link playwright-mcp-cli "npx -y @playwright/mcp@latest --headless --isolated"
```
## Shared Profile Dual CLI (Persistent Login State)
Use this mode when you need to preserve login/session state across runs.
```bash
command -v playwright-mcp-headless
command -v playwright-mcp-ui
uxc link playwright-mcp-headless "npx -y @playwright/mcp@latest --headless --user-data-dir ~/.uxc/playwright-profile"
uxc link playwright-mcp-ui "npx -y @playwright/mcp@latest --user-data-dir ~/.uxc/playwright-profile"
```
Do not run these two links concurrently with the same profile directory.
Switch serially:
```bash
uxc daemon stop
playwright-mcp-ui -h
```
## Discover And Inspect
```bash
playwright-mcp-cli -h
playwright-mcp-cli browser_navigate -h
playwright-mcp-cli browser_snapshot -h
```
## Read-First Flow
Navigate first:
```bash
playwright-mcp-cli browser_navigate url=https://example.com
```
Capture accessibility snapshot:
```bash
playwright-mcp-cli browser_snapshot
```
## Action Flow (Confirm High-Impact Actions First)
Click by snapshot reference:
```bash
playwright-mcp-cli browser_click ref=e6
```
Run custom script:
```bash
playwright-mcp-cli browser_run_code code='await page.waitForTimeout(1000)'
```
## Bare JSON Positional Example
```bash
playwright-mcp-cli browser_navigate '{"url":"https://example.com"}'
```
## Output Parsing
Rely on envelope fields:
- Success: `ok == true`, consume `data`
- Failure: `ok == false`, inspect `error.code` and `error.message`
## Fallback Equivalence
- `playwright-mcp-cli <operation> ...` is equivalent to `uxc "npx -y @playwright/mcp@latest --headless --isolated" <operation> ...`.
- If link setup is temporarily unavailable, use the direct `uxc "<endpoint>" ...` form as fallback.
FILE:scripts/validate.sh
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "BASH_SOURCE[0]")/../../.." && pwd)"
SKILL_DIR="ROOT_DIR/skills/playwright-mcp-skill"
SKILL_FILE="SKILL_DIR/SKILL.md"
OPENAI_FILE="SKILL_DIR/agents/openai.yaml"
fail() {
printf '[validate] error: %s\n' "$*" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || fail "required command not found: $1"
}
need_cmd rg
required_files=(
"SKILL_FILE"
"OPENAI_FILE"
"SKILL_DIR/references/usage-patterns.md"
)
for file in "required_files[@]"; do
[[ -f "file" ]] || fail "missing required file: file"
done
if ! head -n 1 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! tail -n +2 "SKILL_FILE" | rg -q '^---$'; then
fail "SKILL.md must include YAML frontmatter"
fi
if ! rg -q '^name:\s*playwright-mcp-skill\s*$' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define: name: playwright-mcp-skill"
fi
if ! rg -q '^description:\s*.+' "SKILL_FILE"; then
fail "SKILL.md frontmatter must define a description"
fi
if ! rg -q 'npx -y @playwright/mcp@latest --headless --isolated' "SKILL_FILE"; then
fail "SKILL.md must document fixed Playwright MCP stdio endpoint"
fi
if ! rg -q 'command -v playwright-mcp-cli' "SKILL_FILE"; then
fail "SKILL.md must include link command existence check"
fi
if ! rg -q 'uxc link playwright-mcp-cli "npx -y @playwright/mcp@latest --headless --isolated"' "SKILL_FILE"; then
fail "SKILL.md must include fixed link creation command"
fi
if ! rg -q 'playwright-mcp-headless' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include optional shared-profile headless command"
fi
if ! rg -q 'playwright-mcp-ui' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include optional shared-profile headed command"
fi
if ! rg -q -- '--user-data-dir' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include shared profile guidance via --user-data-dir"
fi
if ! rg -q 'uxc daemon stop' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include daemon stop guidance before switching shared-profile links"
fi
if ! rg -q 'playwright-mcp-cli -h' "SKILL_FILE"; then
fail "SKILL.md must use playwright-mcp-cli help-first discovery"
fi
if ! rg -q 'playwright-mcp-cli browser_navigate -h' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include operation-level help via playwright-mcp-cli <operation> -h"
fi
if ! rg -q 'browser_navigate url=' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include key=value example"
fi
if ! rg -q "browser_navigate .*'\\{.*\\}'" "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include bare JSON positional example"
fi
if ! rg -q 'official source' "SKILL_FILE"; then
fail "SKILL.md must mention official-source discovery step"
fi
if ! rg -q 'probe candidate endpoints with' "SKILL_FILE"; then
fail "SKILL.md must include probe step before finalizing endpoint"
fi
if ! rg -q 'no OAuth/API key' "SKILL_FILE"; then
fail "SKILL.md must explicitly document auth detection result"
fi
if rg -q -- 'playwright-mcp-cli (list|describe|call)|--input-json|--args .*\{' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must not use legacy list/describe/call/--input-json/--args JSON forms"
fi
if rg -qi 'retry with .*suffix|append.*suffix|dynamic rename|auto-rename' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must not include dynamic command renaming guidance"
fi
if ! rg -q 'references/usage-patterns.md' "SKILL_FILE"; then
fail "SKILL.md must reference usage-patterns.md"
fi
if ! rg -q 'equivalent to `uxc "npx -y @playwright/mcp@latest --headless --isolated"' "SKILL_FILE" "SKILL_DIR/references/usage-patterns.md"; then
fail "docs must include fallback equivalence guidance"
fi
if ! rg -q '^\s*display_name:\s*"Playwright MCP"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.display_name"
fi
if ! rg -q '^\s*short_description:\s*".+"\s*$' "OPENAI_FILE"; then
fail "agents/openai.yaml must define interface.short_description"
fi
if ! rg -q '^\s*default_prompt:\s*".*\$playwright-mcp-skill.*"\s*$' "OPENAI_FILE"; then
fail 'agents/openai.yaml default_prompt must mention $playwright-mcp-skill'
fi
echo "skills/playwright-mcp-skill validation passed"