Skip to content

About

πŸ”Œ connectory: a directory of the platforms a program might want to talk to, and each API as one Lua file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

15 Commits

Folders and files

Repository files navigation

πŸ”Œ connectory

A directory of the platforms a program might want to talk to, and what it takes to talk to them: where the API lives, what signs a request, what you can ask it to do, what it looks like, and whether it runs an MCP server of its own.

Every API is also one Lua file. GitHub's entire REST API is 1,201 operations in 249 KB that stock Lua loads in under two milliseconds. With the port in lua/, that file is a complete client for an agent: no SDK, no service in the middle, no C modules. The port is built for Tablua's harness, and any Lua host can give it the three things it needs.

local connect = require "connectory.lua.connect"
local c = connect.new({ fetch = your_fetch }, {
  read = function (path) return read_file("connectory/" .. path) end,
  secret = function (name) return keychain(name) end,   -- the person's own credential, by name
})

c:find("issue tracker")                        -- { { service = "github", ... }, ... }
c:operations("github", "create an issue")      -- { { op = "github.issues_create", args = { "owner*", ... } } }
local issue, err = c:call("github.issues_create", { owner = "you", repo = "yours", title = "it works" })
-- with no credential: err.code == "denied", and err.needs says what to ask the person for
853 platforms
842 with a mark (99%)
79 with an MCP server the vendor publishes
758 callable with a token from the environment

Why it is built this way

A credential is a name and nothing else. There is no OAuth dance here and no service holding your tokens. auth.env names the credential (STRIPE_SECRET_KEY); the host's secret(name) reads it from wherever the person keeps it (a keychain, or the environment) at the moment a call is signed. That is why this repository can be public: there is nothing in it to leak.

Only the vendor's own description is used. Fuzzy matching finds klarna.com:openai for OpenAI and Sunshine Conversations for Zendesk β€” right organisation, wrong product. A wrong parameter list is worse than an empty one: an empty catalog says "I cannot do that", while a wrong one builds a call that fails at the vendor or, worse, succeeds and does something else. So a description is accepted from the vendor's own repository, from their own documentation site, from an exact domain match, or from a person who checked it. Nowhere else.

A platform nobody describes is still listed, with a name, a logo, a base URL and a credential β€” everything except operations. Where the vendor publishes documentation a program can read, that is kept beside it as docs.md: context for a reader, never a source of endpoints. An endpoint inferred from prose is a guess wearing the costume of a fact.

That is why the number of platforms and the number with operations are different numbers, and both are printed.

Nothing here is invented. Every operation comes from a machine-readable description the vendor publishes. No hand-written endpoints, because a hand-written endpoint is a guess that looks like a fact.

The one thing written rather than sourced is a sentence for an operation whose description the vendor left empty β€” thousands arrive with a method, a path and nothing else, which leaves them unfindable, because a ranker matching words has no words to match. GET /V1.0/ActionTypes becomes "Lists action types." It describes the shape of the call and never its meaning, it is derived from the path the vendor published, and it is marked described: derived so it is never mistaken for their words.

What is in a provider

providers/github/
  github.lua     every call it accepts: method, URL, where each argument goes
  index.json     names, descriptions and argument types β€” what you need to FIND a call
  logo.svg
  mcp.json       the MCP server the vendor publishes, if there is one
  docs.md        their documentation, when there is no description to be had
  alias.json     "the same API as this other one, with a different credential"

The split between github.lua and index.json is deliberate. For GitHub the calling table is 317 KB and the prose and schemas around it are 1.3 MB. A program that already knows which operation it wants should not carry the half that only helps a person choose.

index.json at the root is the whole directory as one searchable file, small enough to hold in memory. Operations are not in it, on purpose.

Keeping up

APIs move. tools/packs.py asks each vendor whether their description changed using an HTTP conditional request, so the usual answer is 304 and costs nothing. When the bytes do differ the pack is rebuilt and the change is reported in operations, not lines β€” a vendor reformatting their JSON is not an API change, and an operation disappearing very much is. A scheduled job runs it and opens a pull request when something moved.

python3 tools/directory.py   who exists, and how a request to them is signed
python3 tools/specs.py       whose description belongs to whom, and who published it
python3 tools/discover.py    the same, looked for on each vendor's documentation site
python3 tools/context.py     documentation for the platforms nobody describes
python3 tools/build.py       the packs, from those descriptions
python3 tools/logos.py       a mark for every platform
python3 tools/mcp.py         which of them run an MCP server
python3 tools/index.py       the searchable index

Calling a provider, and its tests

lua/connect.lua is the port an agent uses: find a service, list its calls, make one, and say what to ask the person for when a credential is missing or refused. lua/http.lua builds and signs each call the way the provider's pack says (auth: a header in the vendor's own format, a bearer token, a query parameter, or a user and password) with values it asks the host for by name, and refuses a call whose credential is missing before anything is sent. lua/library.md is the card an agent reads.

The tests run from the folder above this one, with Tablua's core/ on the path for its JSON:

luajit -e 'package.path = "./?.lua;TABLUA/core/?.lua;" .. package.path' connectory/lua/http_test.lua
luajit -e 'package.path = "./?.lua;TABLUA/core/?.lua;" .. package.path' connectory/lua/connect_test.lua

Moonsplice attaches this repository as a submodule, and its agent, Tablua, reaches services through it: it finds a service, reads its calls, asks before any call that changes something, and makes calls with the person's own account. When a credential is missing, the person is asked in a masked field or a prompt that does not echo, and the value goes to the keychain, never to the agent.

Where the facts come from

The directory of who-exists-and-how-they-authenticate is generated from NangoHQ/nango's provider registry, which maintains the half of an integration that no API description contains β€” base URL, credential, which header carries it and in what format, and the per-tenant values a URL needs. It contains no operations at all; those come from each vendor's own OpenAPI or Google discovery document.

Marks come from Simple Icons (CC0) where they have one, and from Nango's provider logos otherwise. They are trademarks of the companies they belong to, used to identify the vendor whose API it is, unaltered.

MCP servers come from the official registry and are matched on the reverse-DNS namespace a vendor publishes under, which is the distinction that namespace exists to make.

License

Apache 2.0. The API descriptions and marks belong to the companies that publish them; see LICENSE for the details.

About

πŸ”Œ connectory: a directory of the platforms a program might want to talk to, and each API as one Lua file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages