Skip to main content
ClaudeWave

Agent tools for any API, device, or local process, from a description you write or compile from OpenAPI. HTTP, MQTT, RTSP, in-process, subprocess, or your own protocol. Authorized per operation; the agent never holds your keys.

MCP ServersRegistry oficial13 estrellas9 forksPythonApache-2.0Actualizado today
Install in Claude Code / Claude Desktop
Method: pip / Python · thingctx
Claude Code CLI
claude mcp add thingctx -- python -m thingctx
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "thingctx": {
      "command": "python",
      "args": ["-m", "thingctx"]
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
💡 Install first: pip install thingctx
Casos de uso

Resumen de MCP Servers

# thingctx

[![CI](https://img.shields.io/github/actions/workflow/status/thingctx/thingctx/ci.yml?branch=main&style=flat-square&label=CI)](https://github.com/thingctx/thingctx/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/thingctx?style=flat-square&label=PyPI&color=3775A9)](https://pypi.org/project/thingctx/)
[![Python](https://img.shields.io/badge/Python-3.10%2B-3776AB?style=flat-square)](https://pypi.org/project/thingctx/)
[![License](https://img.shields.io/badge/License-Apache--2.0-4c9a2a?style=flat-square)](LICENSE)
![W3C WoT](https://img.shields.io/badge/W3C_WoT-TD_1.1-005a9c?style=flat-square)

![thingctx gating and routing a tool call](assets/hero.gif)

**thingctx turns a description of any API, device, or local process into
tools an AI agent can call: authorized per operation, over the system's own
transport, and the agent never holds your keys.** The description is a
[W3C Web of Things](https://www.w3.org/WoT/) Thing Description (a TD), a
plain JSON file naming what the system can do. Write one, or compile it from
an OpenAPI spec. The described system is the server, so you run no server
per integration.

A "Thing" is whatever you want to describe: a REST API, an app, a sensor, a
media server, a local object, or one system that is several of those at
once. A media server takes commands over HTTP and serves its stream over
RTSP. The pump in [`examples/`](examples/) reads over MQTT and acts through
a local call. Each is one Thing and one description.

A description names `actions`, `properties`, and `events`, and gives each one
a form: the entry that says where and how to reach it. The URL scheme in that
address picks the transport per call, so however many protocols one Thing
spans, the agent still sees one tool set.

[`docs/BINDINGS.md`](docs/BINDINGS.md) covers the transports and how to add
your own. A subprocess transport, `exec`, also ships, and it ships locked:
it refuses every command until you hand it an explicit allowlist.

Those three stay distinct all the way to the call. A read, a write, and a
subscribe do not collapse into one undifferentiated function, which is why
the gate can allow the read and refuse the write on the same system. A flat
list of functions has no such handle.

Pick your path: [use it as a
library](#use-it-as-a-library) if you own the agent loop, [the command
line](#the-command-line) if you do not want to write code, [the MCP
bridge](#the-mcp-bridge) if your host only speaks MCP, or [add a
transport](#extend-it) if thingctx does not speak your protocol yet.

Stuck, or wondering whether something is possible? Ask in
[Discussions](https://github.com/thingctx/thingctx/discussions). Want to build
something small? The [open issues](#contributing) say what would help.

## First run: no keys, no network

One paste shows the whole idea. The description is inline and the handler
ships with the package, so nothing here needs a key, a network, or a second
file:

```bash
pip install thingctx
```

```python
import asyncio

import thingctx
from thingctx.contrib.time import make_time_handler

TD = {
    "@context": "https://www.w3.org/2022/wot/td/v1.1",
    "id": "urn:thingctx:time",
    "title": "Time",
    "securityDefinitions": {"nosec_sc": {"scheme": "nosec"}},
    "security": ["nosec_sc"],
    "actions": {
        "getCurrentTime": {
            "description": "Current time in an IANA timezone (default UTC).",
            "input": {
                "type": "object",
                "properties": {"timezone": {"type": "string"}},
            },
            "safe": True,
            "idempotent": True,
            "forms": [{"href": "local://getCurrentTime"}],
        }
    },
}


async def main():
    client = thingctx.ThingClient(
        tds=[TD], bindings=[thingctx.LocalBinding(make_time_handler())]
    )
    tools, invoke = client.as_tools()  # specs for your model; invoke runs a call
    print("tools:", [t["function"]["name"] for t in tools])
    print(await invoke("time__getCurrentTime", {"timezone": "UTC"}))


asyncio.run(main())
```

```
tools: ['time__getCurrentTime']
{'timezone': 'UTC', 'datetime': '2026-07-26T15:59:03.233080+00:00', 'utc_offset': '+0000'}
```

That is the whole loop: a description in, tools out, calls routed.
`LocalBinding` is a binding, a transport implementation; this one routes the
`local://` address to the handler you pass it. Every other transport works
the same way; only the form's `href` changes.

## Install

```bash
pip install 'thingctx[llm,http,validate]'
```

Quote the argument; unquoted brackets fail in zsh. Base
`pip install thingctx` has no dependencies; it already includes the `local`
and `exec` transports. Every optional transport and capability has an extra,
listed in [`pyproject.toml`](pyproject.toml); each one this page uses is
named next to the code that needs it.

## The document

A description can be small. This one drives the live, key free
[Open-Meteo](https://open-meteo.com) forecast API, so it runs as written.
Save it as `weather.td.json`, a later example uses it:

```json
{
  "@context": "https://www.w3.org/2022/wot/td/v1.1",
  "id": "urn:example:weather:v1",
  "title": "Weather",
  "securityDefinitions": { "nosec_sc": { "scheme": "nosec" } },
  "security": ["nosec_sc"],
  "actions": {
    "forecast": {
      "input": {
        "type": "object",
        "properties": {
          "latitude": { "type": "number" },
          "longitude": { "type": "number" },
          "current": { "type": "string" }
        },
        "required": ["latitude", "longitude"]
      },
      "forms": [{ "href": "https://api.open-meteo.com/v1/forecast", "htv:methodName": "GET" }]
    }
  }
}
```

thingctx projects each action to a tool named `<thing>__<action>`: this file
yields `weather__forecast` (a version segment on the id is dropped).
[`docs/MAPPING.md`](docs/MAPPING.md) specifies the full projection. The
description carries no secrets, so it is safe to commit and share. The
secret is supplied at runtime to the binding that carries the call. Secrets
are keyed per Thing.

If your system already has an OpenAPI 3.x spec, you do not have to author a
description at all:

```bash
pip install 'thingctx[openapi]'
thingctx import openapi https://api.example.com/openapi.json --out api.td.json
```

The spec can be a file or a URL, JSON or YAML; `--base-url`, `--id`, and
`--title` override what the spec says. The result is a normal description,
so everything else here applies to it unchanged.

Ready made descriptions for common apps, developer tools, and devices live
at [td.thingctx.com](https://td.thingctx.com).

## Use it as a library

Own the agent loop? Read descriptions, hand the specs to your model, and
route each call back through `invoke`. `ThingClient` needs no LLM and has no
opinion on what chose the action; any code can drive it. This sketch shows
the surface against a folder describing a pump; `from_arg` turns a path or
URL into a registry of descriptions:

```python
import thingctx


async def run():  # a sketch, not a program: call it from your own loop
    client = thingctx.ThingClient.from_registry(thingctx.from_arg("./descriptions/"))
    specs, invoke = client.as_tools()

    await invoke("pump__set_speed", {"rpm": 1500})
    await client.read_property("pump__rpm")
    await client.write_property("pump__target_rpm", 1500)   # gated like invoke
    async for evt in await client.subscribe("pump__overheat"):  # subscribe returns an async iterator
        ...
```

The form picks the transport per call, so one client can read over HTTP and
subscribe over MQTT. Bindings that pull optional dependencies, MQTT among
them, are off by default: install the extra and pass
`bindings=thingctx.BindingRegistry.default(mqtt=True)`.

Want the loop handled for you? The `llm` extra runs any provider through
litellm; add the `http` extra, since the weather file's form is HTTPS. Set
your provider key in its usual variable (`OPENAI_API_KEY`,
`ANTHROPIC_API_KEY`, and so on) and pick a model with `THINGCTX_MODEL`:

```python
import asyncio
import thingctx


async def main():
    # weather.td.json from above; it points at a live, key free API
    host = thingctx.from_file("weather.td.json")
    print(await host.chat("What is the forecast for Cairo? Latitude 30.0, longitude 31.2."))


asyncio.run(main())
```

The model calls `weather__forecast` itself and answers in prose:

```
The forecast for Cairo is clear, around 34 C.
```

## Safety: approval and authorization

Two opt in layers stand between an agent and a real system; both run
before any transport is selected. Neither layer handles a credential. The
binding holds the secret and the model never sees it.

**Approval** gates risky calls behind a human. Risk is read from the
description, and when to gate is yours: `declared` (only actions the
description marks risky; the default), `destructive` (adds non idempotent
actions and every property write), `all`, or `never`. A gated call with no
approver is denied: a gate with nobody to open it stays shut. The check sits
inside `invoke` and `write_property`, so it covers the LLM loop, direct
callers, and the MCP bridge alike.

Carrying on from the first example above, with the same `TD`:

```python
def approve(req):  # sync or async; return True to allow
    return input(f"run {req.tool_name}{req.arguments}? [y/N] ").lower() == "y"

client = thingctx.ThingClient(
    tds=[TD],
    bindings=[thingctx.LocalBinding(make_time_handler())],
    approve=approve,
    approve_when="all",
)
```

**Authorization** decides from policy, per caller, per operation. Pass a
`pdp` and an `identity` and every call authorizes the resolved
`(thing, affordance, operation)` before it reaches the system; an affordance
is an action, property, or event named in the description. This snippet condenses
[`examples/14_authz.py`](examples/14_authz.py), which runs as is on the core
install. It defines the `TD` and `Pump` used below: a pump with one
property, `target_rpm`.

```python
from
agent-toolsai-agentsdigital-twinfunction-callingiotjson-ldllmllm-toolsmcpmcp-servermodel-context-protocolmqttopenapipythonrtspthing-descriptiontool-callingw3cweb-of-thingswot

Lo que la gente pregunta sobre thingctx

¿Qué es thingctx/thingctx?

+

thingctx/thingctx es mcp servers para el ecosistema de Claude AI. Agent tools for any API, device, or local process, from a description you write or compile from OpenAPI. HTTP, MQTT, RTSP, in-process, subprocess, or your own protocol. Authorized per operation; the agent never holds your keys. Tiene 13 estrellas en GitHub y se actualizó por última vez today.

¿Cómo se instala thingctx?

+

Puedes instalar thingctx clonando el repositorio (https://github.com/thingctx/thingctx) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.

¿Es seguro usar thingctx/thingctx?

+

thingctx/thingctx aún no ha sido auditado por nuestro agente de seguridad. Revisa el repositorio original en GitHub antes de usarlo en producción.

¿Quién mantiene thingctx/thingctx?

+

thingctx/thingctx es mantenido por thingctx. La última actividad registrada en GitHub es de today, con 32 issues abiertos.

¿Hay alternativas a thingctx?

+

Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.

Despliega thingctx en tu cloud

Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.

¿Mantienes este repo? Añade un badge a tu README

Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.

Featured on ClaudeWave: thingctx/thingctx
[![Featured on ClaudeWave](https://claudewave.com/api/badge/thingctx-thingctx)](https://claudewave.com/repo/thingctx-thingctx)
<a href="https://claudewave.com/repo/thingctx-thingctx"><img src="https://claudewave.com/api/badge/thingctx-thingctx" alt="Featured on ClaudeWave: thingctx/thingctx" width="320" height="64" /></a>

Más MCP Servers

Alternativas a thingctx