LLMs Technical Reviews

metorial/metorial

Catalog of 1,149 TypeScript integrations built on Slates, a JSON-RPC SDK for agent tools, auth flows and trigger groups.

GitHub ↗★ 3.4kTypeScriptcommit 54e6a25 · 2026-10-06homepage ↗

Overview

This repository is Metorial’s integration catalog and the SDK it is written in, not the Metorial platform. It holds 1,149 integration packages under integrations/, plus the Slates stack they target: a typed JSON-RPC protocol (@slates/proto), an authoring SDK (@slates/provider, re-exported as slates), a handler that serves a slate over that protocol (@slates/provider-handler), a client (@slates/client), cross-provider adapters such as chat, and a CLI and test kit. Each integration is a small Bun/TypeScript package that declares auth methods, config, tools and trigger groups with Zod schemas.

What turns a slate into something an agent can call lives elsewhere. Credential storage, the OAuth redirect host, scheduling of polling and token refresh, webhook ingress, MCP exposure and the hosted API all belong to the separate metorial-platform repo and the hosted service. The README’s “Tech Stack” section (Docker MCP containers, a Go engine, MongoDB) describes that wider product. The code here contains no MCP server and no Docker runtime. Integrations are bundled with ncc into single files, and slate.json options such as timeout (capped at 900 s, “the AWS Lambda hard limit”) point to deployment as serverless functions (slate.schema.json).

The code is licensed under FSL-1.1-ALv2: source-available now, converting to Apache 2.0 after two years, with competing commercial use prohibited until then.

Architecture

flowchart LR
  P["Metorial platform (not in repo)"] --> CL["@slates/client"]
  CL --> TR["Transport (local in-process)"]
  TR --> H["provider-handler: JSON-RPC methods"]
  H --> S["Slate: spec + tools + trigger groups"]
  S --> AU["SlateAuth methods (OAuth, token)"]
  S --> TL["SlateTool.handleInvocation"]
  S --> TG["Trigger groups (webhook / polling)"]
  TL --> API["Third-party API (axios)"]
  AD["Adapters, e.g. chat"] --> S
  CLI["slates CLI + test kit"] --> CL
Component Path Role
Integrations integrations/<name>/ 1,149 packages: slate.json manifest, src/index.ts exporting provider = Slate.create(...)
Protocol packages/proto/ Zod schemas for slates/* messages, version string, handler manager
Provider SDK packages/provider/ (+ packages/slates) SlateSpecification, SlateAuth, SlateTool, SlateTrigger, trigger groups, errors, axios helpers
Provider handler packages/provider-handler/ Implements every slates/* request against a Slate: auth, config, actions, triggers
Client packages/client/ Typed client and createLocalSlateTransport
Adapters packages/adapter/, adapters/chat/ Cross-provider contracts (the chat adapter: 28 tools, 9 triggers; implemented by Slack)
Shared helpers packages/oauth-*, *-recipes, slack-tools Reusable OAuth flows and API recipes for families of integrations
CLI and tests packages/cli/, packages/test/, test-integrations/ Run tools and triggers locally against profiles; MCP-compatible schema checks

How a request flows

Take a host invoking Slack’s get_team_info tool:

  1. Connect. The host creates a transport. In this repo that is createLocalSlateTransport, which builds the provider handler in-process and feeds each JSON-RPC message to SlatesProviderProtoHandlerManager.handleInput (transport.ts).
  2. Set context. The host sends notifications: slates/hello (protocol), slates/participant.set, slates/auth.set (the auth output, validated against the spec’s auth schema), slates/config.set and slates/session.start (provider-handler/index.ts). The handler keeps these in local state. It stores nothing.
  3. Discover. slates/actions.list maps each action to id, name, description, tags, scopes, auth methods and JSON Schemas converted from Zod (spec.ts).
  4. Invoke. slates/action.tool.invoke looks up the tool, validates the input against its Zod schema, and builds a SlateContext with config, input and auth output. Public tools get a SlatePublicContext with no auth (provider-handler/index.ts). Missing session context fails with precondition_failed (L304-L329).
  5. Run. handleInvocation runs inside traceProviderCall and runWithContext. Slack’s tool creates a client from ctx.auth.token, calls the API, and returns { output, message } (get-team-info.ts).
  6. Return. Attachments from the result and the context are merged, any auth secrets embedded in attachment URLs are redacted, attachments may be routed through direct upload, and HTTP traces are attached (provider-handler/index.ts). Errors are mapped to slate codes such as request.rate_limited or auth.required (provider.ts).

Key components

Slate and tools

Slate.create({ spec, tools, triggers, adapters, triggerGroups }) merges adapter actions into the action list. It rejects duplicate adapters, conflicting action keys, and more than one manual-webhook trigger group (slate.ts). Tools use a builder: SlateTool.create(spec, { name, key, description, tags }).scopes(...).input(zod).output(zod).handleInvocation(fn).build() (tool.ts). readOnly/destructive tags and per-tool OAuth scope requirements are first-class, so a host can filter tools by what a connection was granted.

Auth methods

SlateAuth.create().output(schema) chains several methods. Slack has bot OAuth, user OAuth and token methods. The integration owns the provider-specific parts: building the authorize URL, exchanging the code at /oauth.v2.access, refreshing tokens, and loading a profile (slack auth.ts). The handler exposes these as slates/auth.authorization_url.get, …callback.handle and …token_refresh.handle. The host passes in client id and secret and keeps the result (provider-handler/index.ts). Deciding when to refresh, and where tokens live, is the platform’s job.

Trigger groups

A trigger group is either polling (with intervalSeconds, default and protocol minimum 600 s) or webhook, with automatic or manual registration and a process handler (triggerGroup.ts, proto triggerGroup.ts). process turns a raw request into events, each with routing matchers and an optional idempotency key. The handler then works out which triggers match each event (provider-handler/index.ts). Slack’s events group shows the pattern well: manual setup instructions, a five-minute replay window, and HMAC-SHA256 verification of v0:timestamp:body (eventsTriggerGroup.ts).

Adapters

An adapter is a cross-provider contract. ChatAdapter lists about 65 capability flags. Each flag maps to metorial_chat$… tools or triggers, or to nothing for pure feature flags such as content_tables (adapter.ts). The contract defines 28 tools and 9 triggers. Any chat provider that implements it lets an agent send a message through the same tool shape. At this commit Slack is the only integration that does.

Extending it

  • New integration. Copy an existing package under integrations/. Add a slate.json (name in @scope/name form, categories, skills), a spec.ts with config and auth, tools built with SlateTool.create, and export provider = Slate.create(...) from src/index.ts (slack index.ts).
  • Reuse. Shared OAuth packages (oauth-google, oauth-microsoft, oauth-zoho) and recipe packages cover provider families.
  • Test locally. bun run integrations:cli <integration> … runs tools, auth setup and trigger processing against saved profiles. @slates/test checks that every tool schema stays MCP-compatible.

Running it

  • Tooling. Bun 1.4 and Turbo. bun run build:integrations builds the packages, then each integration with ncc.
  • Locally. Through the slates CLI or createLocalSlateTransport in your own process. The provider handler needs the host to supply auth output and config. There is no server to start.
  • In production. Through Metorial’s hosted platform or the separately published platform repo, which add the credential vault, OAuth setup sessions, MCP endpoints and the agent SDKs (metorial-node, metorial-python) shown in the README.

Strengths and caveats

  • Strength: breadth with consistent shape. More than a thousand integrations share one SDK, typed Zod I/O, scopes, read-only/destructive tags and structured error codes.
  • Strength: well-designed trigger model. Trigger groups split delivery (webhook or polling) from routing (matchers per trigger), with idempotency keys and real signature checks.
  • Strength: stateless providers. A slate keeps no credentials of its own. All state arrives through protocol messages, which keeps the code easy to sandbox and deploy as functions.
  • Caveat: not runnable as a product on its own. Token storage, refresh scheduling, webhook ingress and MCP exposure are all outside this repo.
  • Caveat: execution is plain in-process code. No retries or rate limiting in the handler. Pagination and backoff are left to each integration’s author.
  • Caveat: stale README. Its stack description (Docker MCP containers, Go, MongoDB) does not match this code base and can mislead evaluators.
  • Caveat: FSL license. It is source-available for two years per release, not OSI open source.

Sources: code at 54e6a25, verified Q&A.

How it answers the API layer & connectors questions

Each answer was drafted by a code-reading agent at commit 54e6a25. Its citations were checked mechanically. Compare with the other api layer & connectors →

How is third-party authentication implemented?

answered

Authentication is a typed method stack per integration via @slates/provider SDK. SlateAuth.create() holds multiple methods (integrations/slack/src/auth.ts:247-458). Four auth types in packages/proto/src/types/authMethod.ts: auth.oauth, auth.token, auth.service_account, auth.custom with typed schemas and capabilities.

OAuth2: platform calls slates/auth.authorization_url.get, invokes getAuthorizationUrl (packages/provider-handler/src/index.ts:709-757). After user auth, slates/auth.authorization_callback.handle invokes handleCallback (packages/provider-handler/src/index.ts:654-707). Slack exchanges code at /oauth.v2.access (integrations/slack/src/auth.ts:281-327).

Token refresh via slates/auth.token_refresh.handle (packages/provider-handler/src/index.ts:815-873; integrations/slack/src/auth.ts:302-323). Credential redaction via AuthConfigSecretRedactor (packages/provider/src/auth/redact.ts:15-60). API key uses getOutput, Slack Bot Token validates xoxb- prefix then auth.test (integrations/slack/src/auth.ts:397-426). Multiple OAuth (bot/user) and token methods coexist per integration.

How is an integration / connector defined?

answered

Each integration is a self-contained TypeScript package under integrations/ with slate.json manifest. 1,149 integration packages. slate.json schema (slate.schema.json:1-43) requires name and supports categories, skills, logoUrl, networkIsolation, and timeout.

Core built via Slate.create() (packages/provider/src/specification/slate.ts:6-92). Takes SlateSpecification (config, auth, metadata), tools, triggers, adapters, trigger groups. Tools via SlateTool.input().output().handleInvocation() builder (packages/provider/src/action/tool.ts:38-48). Triggers via .triggerGroup().matches().map() (packages/provider/src/action/trigger.ts:50-57).

No code generation. Hand-written TypeScript. Protocol version slates@2026-01-01 (packages/proto/src/version.ts:1). Chat adapter defines about 80 tools/triggers (adapters/chat/src/adapter.ts) that integrations implement via .implement() and .register().

Editor's note. Correction: the chat adapter defines 28 tools and 9 triggers (not about 80), mapped from about 65 capability flags in adapters/chat/src/adapter.ts; at this commit only the Slack integration implements it.

How are integrations exposed to LLM agents?

answered

Exposed as JSON-RPC methods via SlatesProviderProtoHandler (packages/proto/src/handler/provider.ts). Provider-handler registers slates/actions.list, slates/action.get, slates/action.tool.invoke, trigger group methods (packages/provider-handler/src/index.ts).

Actions listed via slates/actions.list return id, name, description, JSON Schema input/output, tags, scopes, authMethods (packages/provider-handler/src/spec.ts:122-158). Compatible with LLM function-calling.

External SDKs (metorial-node, metorial-python) wrap Slates protocol for agent frameworks. The Metorial Platform runs MCP servers in Docker (README.md:339-341), external repo. CLI provides listTools, getTool, listTriggerGroups (packages/cli/src/cli.ts:38-80). Chat adapter capabilities enable dynamic tool loading (adapters/chat/src/adapter.ts:6-83).

How is a tool call executed?

answered

Tool calls dispatch via slates/action.tool.invoke handler. Looks up action, validates input against Zod schema, constructs SlateContext or SlatePublicContext, calls handleInvocation wrapped in traceProviderCall (packages/provider-handler/src/index.ts:939-1012).

Direct function dispatch, no sandbox. Rate limiting maps upstream 429 to upstream.rate_limited via mapServiceErrorCodeByStatus (packages/provider/src/error/service.ts:28-43). Error codes for request.rate_limited, upstream.rate_limited, upstream.timeout etc (packages/provider/src/error/defaults.ts:1-50).

No built-in retries. Pagination is integration-authored. Attachments collected, merged with auto-detected URL attachments, redacted via AuthConfigSecretRedactor, optionally directUploaded (packages/provider-handler/src/index.ts:48-164). Context via AsyncLocalStorage (packages/provider/src/context/hook.ts:15-20).

How are data sync, webhooks and triggers implemented?

answered

Triggers organized into trigger groups with two-phase event architecture. Each group has invocation type (polling or webhook) and required routingMatchers handler (packages/proto/src/types/triggerGroup.ts, packages/provider/src/action/triggerGroup.ts).

Webhook groups use autoRegistration (list targets, register) or manualRegistration (setup docs). process handler verifies signatures, extracts events with matchers via slates/trigger_group.webhook.process (packages/proto/src/messages/triggerGroup.ts:240-275). Slack shows HMAC verification, url_verification challenge, authorization resolution (integrations/slack/src/triggers/eventsTriggerGroup.ts:79-198).

Polling groups use min 600s intervals. pollEvents receives prior state, returns events plus updatedState via slates/trigger_group.polling.poll (packages/proto/src/messages/triggerGroup.ts:308-333). Individual triggers have matches() (no auth) and map() (with auth) via slates/action.trigger.map_event. Legacy per-trigger handlers disabled when groups announced (packages/provider-handler/src/index.ts:1014-1094).

How is it self-hosted and what is open vs proprietary?

answered

Licensed under FSL 1.1 with Apache 2.0 future license on second anniversary (LICENSE:1-104). Internal use, non-commercial education/research, and professional services permitted; competing commercial products not.

This repo contains the integration catalog (1,149 integrations with slate.json manifests) and core SDK packages: @slates/proto, @slates/provider, @slates/provider-handler, @slates/adapter, @slates/client, adapters/chat.

Not in repo: Metorial Platform engine (github.com/metorial/metorial-platform, open source, self-hostable), MCP runtime (github.com/metorial/mcp-containers, Docker), cloud dashboard (React), cloud services (PostgreSQL, Redis, MongoDB). README (README.md:77-78) states platform is open source and self-hostable. Required infra: Docker, PostgreSQL, Redis, MongoDB. This repo is the SDK and catalog and requires the platform to run.