@lanes-sh/link 0.6.10 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gcloudignore +25 -15
- package/README.md +62 -45
- package/instructions/skills/lanes-link/SKILL.md +25 -0
- package/package.json +3 -2
- package/src/audit/conformance.ts +1 -1
- package/src/cli/argv.ts +14 -1
- package/src/cli/commands/connect/acquire.ts +100 -0
- package/src/cli/commands/connect/custom/index.ts +1 -1
- package/src/cli/commands/connect/declare.ts +19 -0
- package/src/cli/commands/connect/index.ts +54 -56
- package/src/cli/commands/connect/requirements.ts +23 -1
- package/src/cli/commands/connect/setup.ts +1 -1
- package/src/cli/commands/connect/variables.ts +220 -0
- package/src/cli/commands/knowledge/index.ts +22 -4
- package/src/cli/commands/knowledge/migrate.ts +61 -43
- package/src/cli/commands/operate/tools.ts +1 -1
- package/src/cli/commands/owner/entities.ts +244 -0
- package/src/cli/commands/owner/shared.ts +14 -0
- package/src/cli/commands/owner/vault.ts +2 -2
- package/src/cli/commands/owner.ts +13 -3
- package/src/cli/commands/setup.ts +12 -0
- package/src/cli/config-edit.ts +16 -6
- package/src/cli/config-repair.ts +10 -0
- package/src/cli/dispatch-owner.ts +30 -1
- package/src/cli/main.ts +2 -1
- package/src/cli/runtime/open.ts +14 -7
- package/src/cli/runtime/registry.ts +6 -1
- package/src/cli/selection.ts +11 -0
- package/src/cli/usage.ts +13 -2
- package/src/connectivity/auth/strategy/index.ts +1 -1
- package/src/connectivity/capability.ts +1 -1
- package/src/connectivity/context.ts +1 -1
- package/src/connectivity/index.ts +1 -1
- package/src/connectivity/mail/attachments.ts +42 -73
- package/src/connectivity/mail/content-type.ts +71 -0
- package/src/connectivity/mail/index.ts +1 -1
- package/src/connectivity/manifest/provider.ts +50 -0
- package/src/connectivity/manifest/variables.ts +173 -0
- package/src/connectivity/transports/factory.ts +74 -1
- package/src/connectivity/transports/http/index.ts +8 -2
- package/src/connectivity/transports/http/keys.ts +83 -0
- package/src/connectivity/transports/imap/attachment.ts +53 -10
- package/src/connectivity/transports/imap/capabilities.ts +28 -0
- package/src/connectivity/transports/imap/download.ts +111 -0
- package/src/connectivity/transports/imap/index.ts +13 -0
- package/src/connectivity/transports/imap/operations.ts +1 -0
- package/src/deployments/adapters/s3.ts +1 -1
- package/src/deployments/gcp/provision.ts +1 -1
- package/src/deployments/knowledge.ts +44 -18
- package/src/deployments/prepare.ts +1 -1
- package/src/deployments/serving.ts +1 -1
- package/src/dispatch/dispatch.ts +17 -10
- package/src/dispatch/staging.ts +76 -1
- package/src/profile/knowledge.ts +19 -11
- package/src/profile/schema.ts +2 -2
- package/src/providers/README.md +189 -0
- package/src/providers/airtable/index.ts +10 -0
- package/src/providers/algolia/index.ts +10 -0
- package/src/providers/amplitude/index.ts +10 -0
- package/src/providers/apify/index.ts +10 -0
- package/src/providers/asana/index.ts +10 -0
- package/src/providers/assets/provider.ts +11 -4
- package/src/providers/atlassian/index.ts +19 -0
- package/src/providers/attio/index.ts +10 -0
- package/src/providers/betterstack/index.ts +10 -0
- package/src/providers/box/index.ts +64 -0
- package/src/providers/brightdata/index.ts +10 -0
- package/src/providers/buildkite/index.ts +10 -0
- package/src/providers/bunq/index.ts +2 -2
- package/src/providers/calendly/index.ts +10 -0
- package/src/providers/canva/index.ts +10 -0
- package/src/providers/circleci/index.ts +10 -0
- package/src/providers/clickup/index.ts +10 -0
- package/src/providers/close/index.ts +10 -0
- package/src/providers/cloudflare_bindings/index.ts +14 -0
- package/src/providers/cloudflare_observability/index.ts +10 -0
- package/src/providers/contentful/index.ts +10 -0
- package/src/providers/custom/load.ts +1 -1
- package/src/providers/datadog/index.ts +10 -0
- package/src/providers/discord/index.ts +1 -1
- package/src/providers/discord/specs/discord.v10.json +0 -88
- package/src/providers/discord/specs/vendor.ts +1 -1
- package/src/providers/dropbox/index.ts +10 -0
- package/src/providers/entities/catalogue.ts +366 -0
- package/src/providers/entities/find.ts +279 -0
- package/src/providers/entities/provider.ts +334 -0
- package/src/providers/entities/render.ts +142 -0
- package/src/providers/entities/store.ts +276 -0
- package/src/providers/entities/writes.ts +279 -0
- package/src/providers/example/provider.ts +1 -1
- package/src/providers/expensify/index.ts +10 -0
- package/src/providers/fastmail/calendar/index.ts +21 -0
- package/src/providers/fastmail/contacts/index.ts +17 -0
- package/src/providers/fastmail/index.ts +4 -0
- package/src/providers/fastmail/mail/index.ts +38 -0
- package/src/providers/fastmail/shared/setup.ts +52 -0
- package/src/providers/figma/index.ts +10 -0
- package/src/providers/fireflies/index.ts +10 -0
- package/src/providers/flagsmith/index.ts +10 -0
- package/src/providers/gamma/index.ts +10 -0
- package/src/providers/github/index.ts +2 -2
- package/src/providers/google/gmail/attachment.ts +16 -1
- package/src/providers/google/gmail-imap/index.ts +7 -2
- package/src/providers/google/shared/oauth.ts +1 -1
- package/src/providers/google/shared/service-account.ts +1 -1
- package/src/providers/google/shared/setup.ts +1 -1
- package/src/providers/google/specs/calendar.v3.json +0 -32
- package/src/providers/google/specs/docs.v1.json +0 -76
- package/src/providers/google/specs/drive.v3.json +0 -32
- package/src/providers/google/specs/gmail.v1.json +0 -76
- package/src/providers/google/specs/people.v1.json +0 -77
- package/src/providers/google/specs/sheets.v4.json +0 -76
- package/src/providers/google/specs/tasks.v1.json +0 -76
- package/src/providers/grafana/index.ts +10 -0
- package/src/providers/heroku/index.ts +10 -0
- package/src/providers/hubspot/index.ts +75 -0
- package/src/providers/hygraph/index.ts +10 -0
- package/src/providers/icloud/mail/index.ts +5 -0
- package/src/providers/icloud/shared/setup.ts +1 -1
- package/src/providers/index.ts +255 -0
- package/src/providers/insightly/index.ts +10 -0
- package/src/providers/jam/index.ts +10 -0
- package/src/providers/klaviyo/index.ts +10 -0
- package/src/providers/mailbox/index.ts +87 -0
- package/src/providers/memory/provider.ts +3 -8
- package/src/providers/mercury/index.ts +10 -0
- package/src/providers/microsoft/calendar/index.ts +49 -0
- package/src/providers/microsoft/contacts/index.ts +50 -0
- package/src/providers/microsoft/drive/index.ts +62 -0
- package/src/providers/microsoft/index.ts +12 -0
- package/src/providers/microsoft/mail/index.ts +76 -0
- package/src/providers/microsoft/shared/oauth.ts +58 -0
- package/src/providers/microsoft/shared/scopes.ts +56 -0
- package/src/providers/microsoft/shared/setup.ts +45 -0
- package/src/providers/microsoft/specs/microsoft-todo.v1.json +755 -0
- package/src/providers/microsoft/specs/onedrive.v1.json +993 -0
- package/src/providers/microsoft/specs/outlook-calendar.v1.json +1743 -0
- package/src/providers/microsoft/specs/outlook-contacts.v1.json +430 -0
- package/src/providers/microsoft/specs/outlook-mail.v1.json +901 -0
- package/src/providers/microsoft/specs/vendor.ts +232 -0
- package/src/providers/microsoft/todo/index.ts +54 -0
- package/src/providers/miro/index.ts +10 -0
- package/src/providers/mixpanel/index.ts +10 -0
- package/src/providers/monday/index.ts +10 -0
- package/src/providers/mux/index.ts +10 -0
- package/src/providers/navan/index.ts +10 -0
- package/src/providers/neon/index.ts +10 -0
- package/src/providers/netlify/index.ts +10 -0
- package/src/providers/nextcloud/calendar/index.ts +28 -0
- package/src/providers/nextcloud/contacts/index.ts +18 -0
- package/src/providers/nextcloud/index.ts +3 -0
- package/src/providers/nextcloud/shared/setup.ts +59 -0
- package/src/providers/owner.ts +15 -3
- package/src/providers/paddle/index.ts +10 -0
- package/src/providers/paypal/index.ts +10 -0
- package/src/providers/posthog/index.ts +10 -0
- package/src/providers/prisma/index.ts +10 -0
- package/src/providers/ramp/index.ts +10 -0
- package/src/providers/recurly/index.ts +10 -0
- package/src/providers/reddit/index.ts +1 -1
- package/src/providers/remote/index.ts +10 -0
- package/src/providers/render/index.ts +35 -0
- package/src/providers/replicate/index.ts +10 -0
- package/src/providers/resend/index.ts +10 -0
- package/src/providers/riverside/index.ts +10 -0
- package/src/providers/rootly/index.ts +10 -0
- package/src/providers/rudderstack/index.ts +10 -0
- package/src/providers/salesloft/index.ts +10 -0
- package/src/providers/sanity/index.ts +10 -0
- package/src/providers/scopes.ts +2 -0
- package/src/providers/sentry/index.ts +10 -0
- package/src/providers/setup/plan.ts +11 -0
- package/src/providers/setup/provider.ts +12 -0
- package/src/providers/shared/openapi.ts +70 -0
- package/src/providers/shared/slug.ts +28 -0
- package/src/providers/shared/vendor-operations.ts +43 -1
- package/src/providers/shared/vendor-spec.ts +98 -17
- package/src/providers/shortcut/index.ts +10 -0
- package/src/providers/slack/index.ts +2 -2
- package/src/providers/square/index.ts +10 -0
- package/src/providers/storyblok/index.ts +10 -0
- package/src/providers/stripe/index.ts +10 -0
- package/src/providers/supabase/index.ts +10 -0
- package/src/providers/tasks/store.ts +2 -7
- package/src/providers/tavily/index.ts +10 -0
- package/src/providers/todoist/index.ts +10 -0
- package/src/providers/untested.ts +105 -0
- package/src/providers/vault/provider.ts +1 -1
- package/src/providers/vercel/index.ts +10 -0
- package/src/providers/vimeo/index.ts +10 -0
- package/src/providers/webflow/index.ts +10 -0
- package/src/providers/whimsical/index.ts +10 -0
- package/src/providers/wix/index.ts +10 -0
- package/src/providers/workable/index.ts +10 -0
- package/src/providers/yahoo_mail/index.ts +73 -0
- package/src/providers/zapier/index.ts +10 -0
- package/src/providers/zoho_mail/index.ts +79 -0
- package/src/secrets/document.ts +1 -1
- package/src/server/attachments.ts +129 -5
- package/src/server/index.ts +3 -3
- package/src/server/mcp/instructions.ts +88 -18
- package/src/server/stdio.ts +1 -1
package/src/dispatch/staging.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import type { AuditSink } from '#audit';
|
|
3
3
|
import type { Principal } from '#auth';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
getStaged,
|
|
6
|
+
newHandle,
|
|
7
|
+
putStaged,
|
|
8
|
+
sweepStaged,
|
|
9
|
+
STAGED_TTL_MS,
|
|
10
|
+
type StagedFile,
|
|
11
|
+
} from '#connectivity/mail';
|
|
5
12
|
import type { BlobStore } from '#stores/blobs';
|
|
6
13
|
import { scopeBlobStore } from '#stores/blobs';
|
|
7
14
|
import { scopeNamespace } from './context.ts';
|
|
@@ -100,3 +107,71 @@ export async function stageAttachment(
|
|
|
100
107
|
|
|
101
108
|
return { handle, sha256, expiresAt };
|
|
102
109
|
}
|
|
110
|
+
|
|
111
|
+
export interface FetchStagedRequest {
|
|
112
|
+
readonly principal: Principal;
|
|
113
|
+
readonly providerId: string;
|
|
114
|
+
readonly connectionId: string;
|
|
115
|
+
readonly handle: string;
|
|
116
|
+
readonly clientLabel?: string | undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Handing bytes back out, which is the half staging was missing.
|
|
121
|
+
*
|
|
122
|
+
* Upload always worked: bytes in over HTTP, a handle through the model, a send
|
|
123
|
+
* that names it. The other direction had no route at all — an attachment that
|
|
124
|
+
* arrived by mail could be *described* by `get_message` and attached to an
|
|
125
|
+
* outgoing message by `send_message`, and there was no way to simply have it.
|
|
126
|
+
* The only paths out were to mail it somewhere else or to base64 it through the
|
|
127
|
+
* conversation, which is the cost this whole design exists to avoid.
|
|
128
|
+
*
|
|
129
|
+
* So it is the same shape run backwards, and deliberately the same scoping: the
|
|
130
|
+
* store is namespaced by provider and connection here exactly as it is above, so
|
|
131
|
+
* a handle minted for `icloud_mail.main` is fetchable from that connection and
|
|
132
|
+
* from no other. Nothing is bridged and no provider gains reach it did not have
|
|
133
|
+
* — the bytes were already inside this account's namespace, and this is a door
|
|
134
|
+
* onto it rather than a route between two.
|
|
135
|
+
*
|
|
136
|
+
* Null for "no such handle", "expired", and "wrong connection" alike. A caller
|
|
137
|
+
* cannot act on the difference, and distinguishing them would let someone probe
|
|
138
|
+
* for handles staged against an account they cannot reach.
|
|
139
|
+
*/
|
|
140
|
+
export async function fetchStaged(
|
|
141
|
+
deps: {
|
|
142
|
+
readonly storage: BlobStore;
|
|
143
|
+
readonly audit: AuditSink;
|
|
144
|
+
readonly profile: string;
|
|
145
|
+
},
|
|
146
|
+
request: FetchStagedRequest,
|
|
147
|
+
): Promise<StagedFile | null> {
|
|
148
|
+
const storage = scopeBlobStore(
|
|
149
|
+
deps.storage,
|
|
150
|
+
scopeNamespace(request.providerId, request.connectionId),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const found = await getStaged(storage, request.handle);
|
|
154
|
+
|
|
155
|
+
// Logged either way. "Did anything leave the endpoint" is the question this
|
|
156
|
+
// answers, and a miss is as worth having as a hit — a run of them against
|
|
157
|
+
// handles that do not exist is what probing looks like.
|
|
158
|
+
await deps.audit.append({
|
|
159
|
+
profile: deps.profile,
|
|
160
|
+
principal: request.principal.id,
|
|
161
|
+
...(request.clientLabel ? { clientLabel: request.clientLabel } : {}),
|
|
162
|
+
provider: request.providerId,
|
|
163
|
+
connection: `${request.providerId}.${request.connectionId}`,
|
|
164
|
+
capability: 'attachments.fetch',
|
|
165
|
+
arguments: {
|
|
166
|
+
handle: request.handle,
|
|
167
|
+
...(found
|
|
168
|
+
? { filename: found.filename, bytes: found.bytes.byteLength, content_type: found.contentType }
|
|
169
|
+
: {}),
|
|
170
|
+
},
|
|
171
|
+
authorization: 'allowed',
|
|
172
|
+
status: found ? 'ok' : 'error',
|
|
173
|
+
durationMs: 0,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return found;
|
|
177
|
+
}
|
package/src/profile/knowledge.ts
CHANGED
|
@@ -2,21 +2,28 @@ import { z } from 'zod';
|
|
|
2
2
|
import { credentialRef } from './primitives.ts';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Where a profile's memory and
|
|
6
|
-
* else lives.
|
|
5
|
+
* Where a profile's memory, skills and entities live, when that is not where
|
|
6
|
+
* everything else lives.
|
|
7
7
|
*
|
|
8
8
|
* A target's `storage:` block names one backend for everything a profile holds
|
|
9
9
|
* — runtime state, the audit log, memory entries, skills, cached attachments.
|
|
10
|
-
* That is right for most of it and wrong for
|
|
11
|
-
* artefacts of one installation, and memory entries and
|
|
12
|
-
* the owner wrote. Documents want history, review, and to be
|
|
13
|
-
* than one machine, and the backend that suits an object
|
|
14
|
-
* the backend that suits those.
|
|
10
|
+
* That is right for most of it and wrong for three: state and the log are
|
|
11
|
+
* artefacts of one installation, and memory entries, skills and entity files
|
|
12
|
+
* are documents the owner wrote. Documents want history, review, and to be
|
|
13
|
+
* readable from more than one machine, and the backend that suits an object
|
|
14
|
+
* nobody reads is not the backend that suits those.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
16
|
+
* ADR-041 said "memory and skills, and nothing else", and ADR-056 amends the
|
|
17
|
+
* count without touching the rule. That exclusion was never arithmetic: its
|
|
18
|
+
* argument is a discriminator — an artefact of one installation stays, a
|
|
19
|
+
* document the owner wrote may move — and an entity file is a Markdown document
|
|
20
|
+
* with frontmatter, hand-editable, wanting history and review. It is on the
|
|
21
|
+
* same side of that line as a memory entry, by the same test.
|
|
22
|
+
*
|
|
23
|
+
* So this block moves exactly those three, and it is the whole of what it can
|
|
17
24
|
* move. There is no `credentials` or `vault` value here and there will not be:
|
|
18
25
|
* a repository is a place to publish, and those two hold the material whose
|
|
19
|
-
* entire value is that it is not published (`docs/
|
|
26
|
+
* entire value is that it is not published (`https://lanes.sh/docs/link/security`). The
|
|
20
27
|
* exclusion is structural — a field that does not exist cannot be set by
|
|
21
28
|
* accident, by a flag, or by an operator following an example.
|
|
22
29
|
*
|
|
@@ -89,8 +96,8 @@ export type KnowledgeConfig = z.infer<typeof knowledgeTargetSchema>;
|
|
|
89
96
|
/**
|
|
90
97
|
* Where each area sits inside the repository.
|
|
91
98
|
*
|
|
92
|
-
*
|
|
93
|
-
* memory provider's own blob namespace — the prefix core scopes it into under
|
|
99
|
+
* Three directories, named after the three things that move. `memory` is also
|
|
100
|
+
* the memory provider's own blob namespace — the prefix core scopes it into under
|
|
94
101
|
* the profile's blob root — which is why the same word does both jobs: the
|
|
95
102
|
* route that redirects it and the directory it lands in are the same fact, and
|
|
96
103
|
* spelling them separately is how they would come to disagree.
|
|
@@ -101,6 +108,7 @@ export type KnowledgeConfig = z.infer<typeof knowledgeTargetSchema>;
|
|
|
101
108
|
export const KNOWLEDGE_LAYOUT = {
|
|
102
109
|
memory: 'memory',
|
|
103
110
|
skills: 'skills',
|
|
111
|
+
entities: 'entities',
|
|
104
112
|
} as const;
|
|
105
113
|
|
|
106
114
|
export type KnowledgeArea = keyof typeof KNOWLEDGE_LAYOUT;
|
package/src/profile/schema.ts
CHANGED
|
@@ -116,7 +116,7 @@ export const auditTargetSchema = z.object({
|
|
|
116
116
|
*
|
|
117
117
|
* Its own block rather than a reuse of `credentials`, because the vault must
|
|
118
118
|
* never be the credential store — the two hold different things and collapsing
|
|
119
|
-
* them is the single most damaging mistake available here (`docs/
|
|
119
|
+
* them is the single most damaging mistake available here (`https://lanes.sh/docs/link/security`).
|
|
120
120
|
*
|
|
121
121
|
* Optional, defaulting to `file`: every profile written before ADR-014 keeps
|
|
122
122
|
* working unchanged, and a local run needs no vault configuration at all.
|
|
@@ -242,7 +242,7 @@ export const deployTargetSchema = z.object({
|
|
|
242
242
|
*
|
|
243
243
|
* It is also what makes `limits.requests_per_minute` mean something in
|
|
244
244
|
* aggregate. Those limits are per instance and always were; with no ceiling
|
|
245
|
-
* the aggregate had no value at all, and `docs/
|
|
245
|
+
* the aggregate had no value at all, and `https://lanes.sh/docs/link/deployment-cloudrun`
|
|
246
246
|
* told the reader to cap this themselves because nothing here did.
|
|
247
247
|
*/
|
|
248
248
|
max_instances: z.number().int().min(1).max(100).default(DEPLOY_DEFAULTS.max_instances),
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Providers
|
|
2
|
+
|
|
3
|
+
A hundred and five of them, and almost all are a folder and a line. This page is the inventory and the
|
|
4
|
+
ins and outs; [Connect your accounts](https://lanes.sh/docs/link/connect) is the same list written for someone
|
|
5
|
+
deciding what to connect.
|
|
6
|
+
|
|
7
|
+
## What a provider is
|
|
8
|
+
|
|
9
|
+
Two choices and nothing else: **how a service is reached** (`connector.kind`) and **how we prove who
|
|
10
|
+
we are** (`auth.kind`). Both are closed unions, both are orthogonal, and everything else is data
|
|
11
|
+
hung off the pair. That is why iCloud can speak IMAP with a password while Gmail speaks HTTP with
|
|
12
|
+
OAuth and neither costs the other any code.
|
|
13
|
+
|
|
14
|
+
- [`../connectivity/transports/README.md`](../connectivity/transports/README.md) — the six ways to reach a service
|
|
15
|
+
- [`../connectivity/auth/README.md`](../connectivity/auth/README.md) — the credential types, and the ones not built yet
|
|
16
|
+
- [Connectivity coverage](https://lanes.sh/docs/link/connectivity-coverage) — which pairs work, which are refused, and why
|
|
17
|
+
|
|
18
|
+
Where they come from:
|
|
19
|
+
|
|
20
|
+
| | |
|
|
21
|
+
|---|---|
|
|
22
|
+
| **connector** | `mcp` 77 · `http` 15 · `imap` 6 · `dav` 6 · `fs` 1 |
|
|
23
|
+
| **auth** | `oauth (dynamic)` 70 · `oauth (manual)` 18 · `basic` 12 · `bearer` 2 · `header` 1 · `none` 1 · `strategy` 1 |
|
|
24
|
+
|
|
25
|
+
Two of the hundred and five are code rather than data — `gmail`, which assembles an RFC 2822 message to
|
|
26
|
+
send one, and `bunq`, which runs a signing handshake no declarative form describes. Everything else
|
|
27
|
+
is a manifest.
|
|
28
|
+
|
|
29
|
+
## Tested means somebody connected it
|
|
30
|
+
|
|
31
|
+
**Twenty-one are tested. Eighty-four are not**, and the tables say which.
|
|
32
|
+
|
|
33
|
+
A manifest can be right in every way this repository can check and still not work. What the checks
|
|
34
|
+
prove: the schema validates, the vendored spec generates registrable tools inside the 64 KB budget,
|
|
35
|
+
every scope has a described meaning, the redaction keys name arguments that exist, and — for the
|
|
36
|
+
remote MCP servers — the endpoint answered a live protocol probe with an OAuth challenge.
|
|
37
|
+
|
|
38
|
+
What only a real account proves: that the grant is the right one, that the identity probe reads a
|
|
39
|
+
field the vendor actually returns, that a refresh token comes back, and that the tool list is worth
|
|
40
|
+
having. Until somebody has done that, the honest label is untested — not broken, and not a warning,
|
|
41
|
+
just a fact about what has been established.
|
|
42
|
+
|
|
43
|
+
The list is [`untested.ts`](untested.ts), it is the single source of truth, and
|
|
44
|
+
`../readme.test.ts` asserts the documentation marks exactly those and no others. A provider leaves
|
|
45
|
+
the list when somebody connects it and it does what it says.
|
|
46
|
+
|
|
47
|
+
## Adding one
|
|
48
|
+
|
|
49
|
+
A folder here and a line in [`index.ts`](index.ts). That file says it too, and means it: the folder
|
|
50
|
+
holds *all* of the provider — the manifest, the scopes it asks for, what it redacts from the audit
|
|
51
|
+
log, the setup walkthrough, and any vendored specification.
|
|
52
|
+
|
|
53
|
+
The full walkthrough is [Creating a provider](https://lanes.sh/docs/link/creating-a-provider),
|
|
54
|
+
which opens by pointing out that most providers should not be built-ins at all — a YAML manifest in
|
|
55
|
+
a profile's own `providers.d/` is validated by the same schema and loaded by [`custom/`](custom/).
|
|
56
|
+
|
|
57
|
+
Four things bite, and all four are quiet:
|
|
58
|
+
|
|
59
|
+
1. **A redaction key that misses is invisible.** It withholds every argument and reads exactly like
|
|
60
|
+
working redaction. Key it on the capability name *as served* — Microsoft Graph's operations are
|
|
61
|
+
`me.ListMessages`, dots and all, because `shortenName` strips the provider id and the provider is
|
|
62
|
+
not called "me".
|
|
63
|
+
2. **Schema size is not spec size.** `mcp-from-openapi` inlines `$ref`s, so a request body that
|
|
64
|
+
references a wide entity explodes: Graph's `PATCH /me/messages` measured 346 KB against a 64 KB
|
|
65
|
+
budget. `projectRequestBody` is the remedy where the body is a schema reference, and it is
|
|
66
|
+
checked — a field the vendor renames fails the refresh instead of becoming an argument the API
|
|
67
|
+
ignores.
|
|
68
|
+
3. **A placeholder in the address needs a variable, and the reverse.** A provider whose host belongs
|
|
69
|
+
to the account rather than the vendor declares `variables`; `defineProvider` refuses a
|
|
70
|
+
`{placeholder}` nothing fills and a variable nothing uses, because the first fails as DNS and the
|
|
71
|
+
second asks the operator a question nothing reads. See ADR-055.
|
|
72
|
+
4. **A property key the Anthropic API refuses takes down every provider at once**, not just its own.
|
|
73
|
+
`^[a-zA-Z0-9_.-]{1,64}$`, and one bad key rejects the whole `tools` array. The `http` transport
|
|
74
|
+
renames them and leaves the wire name alone, so OData's `$top` reaches an agent as `top`.
|
|
75
|
+
5. **`base_url` must equal the spec's own `servers[0].url`**, and the version lives in different
|
|
76
|
+
places per vendor — Drive puts it in the host, Sheets in the path. Copying the wrong one 404s
|
|
77
|
+
every call.
|
|
78
|
+
|
|
79
|
+
## The inventory
|
|
80
|
+
|
|
81
|
+
`tested` is blank where nobody has connected it yet.
|
|
82
|
+
|
|
83
|
+
| id | name | connector | auth | tested |
|
|
84
|
+
|---|---|---|---|---|
|
|
85
|
+
| `fastmail_calendar` | Fastmail Calendar | `dav` | `basic` | |
|
|
86
|
+
| `fastmail_contacts` | Fastmail Contacts | `dav` | `basic` | |
|
|
87
|
+
| `icloud_calendar` | iCloud Calendar | `dav` | `basic` | yes |
|
|
88
|
+
| `icloud_contacts` | iCloud Contacts | `dav` | `basic` | yes |
|
|
89
|
+
| `nextcloud_calendar` | Nextcloud Calendar | `dav` | `basic` | |
|
|
90
|
+
| `nextcloud_contacts` | Nextcloud Contacts | `dav` | `basic` | |
|
|
91
|
+
| `icloud_drive` | iCloud Drive | `fs` | `none` | yes |
|
|
92
|
+
| `bunq` | bunq | `http` | `strategy` | yes |
|
|
93
|
+
| `calendar` | Google Calendar | `http` | `oauth (manual)` | yes |
|
|
94
|
+
| `contacts` | Google Contacts | `http` | `oauth (manual)` | yes |
|
|
95
|
+
| `discord` | Discord | `http` | `header` | yes |
|
|
96
|
+
| `docs` | Google Docs | `http` | `oauth (manual)` | yes |
|
|
97
|
+
| `drive` | Google Drive | `http` | `oauth (manual)` | yes |
|
|
98
|
+
| `gmail` | Gmail | `http` | `oauth (manual)` | yes |
|
|
99
|
+
| `google_tasks` | Google Tasks | `http` | `oauth (manual)` | yes |
|
|
100
|
+
| `microsoft_todo` | Microsoft To Do | `http` | `oauth (manual)` | |
|
|
101
|
+
| `onedrive` | OneDrive | `http` | `oauth (manual)` | |
|
|
102
|
+
| `outlook_calendar` | Outlook Calendar | `http` | `oauth (manual)` | |
|
|
103
|
+
| `outlook_contacts` | Outlook Contacts | `http` | `oauth (manual)` | |
|
|
104
|
+
| `outlook_mail` | Outlook Mail | `http` | `oauth (manual)` | |
|
|
105
|
+
| `reddit` | Reddit | `http` | `oauth (manual)` | yes |
|
|
106
|
+
| `sheets` | Google Sheets | `http` | `oauth (manual)` | yes |
|
|
107
|
+
| `fastmail_mail` | Fastmail Mail | `imap` | `basic` | |
|
|
108
|
+
| `gmail_imap` | Gmail (IMAP) | `imap` | `basic` | yes |
|
|
109
|
+
| `icloud_mail` | iCloud Mail | `imap` | `basic` | yes |
|
|
110
|
+
| `mailbox` | Mailbox (any IMAP server) | `imap` | `basic` | |
|
|
111
|
+
| `yahoo_mail` | Yahoo Mail | `imap` | `basic` | |
|
|
112
|
+
| `zoho_mail` | Zoho Mail | `imap` | `basic` | |
|
|
113
|
+
| `airtable` | Airtable | `mcp` | `oauth (dynamic)` | |
|
|
114
|
+
| `algolia` | Algolia | `mcp` | `oauth (dynamic)` | |
|
|
115
|
+
| `amplitude` | Amplitude | `mcp` | `oauth (dynamic)` | |
|
|
116
|
+
| `apify` | Apify | `mcp` | `oauth (dynamic)` | |
|
|
117
|
+
| `asana` | Asana | `mcp` | `oauth (dynamic)` | |
|
|
118
|
+
| `atlassian` | Atlassian | `mcp` | `oauth (dynamic)` | |
|
|
119
|
+
| `attio` | Attio | `mcp` | `oauth (dynamic)` | |
|
|
120
|
+
| `betterstack` | Better Stack | `mcp` | `oauth (dynamic)` | |
|
|
121
|
+
| `box` | Box | `mcp` | `oauth (manual)` | |
|
|
122
|
+
| `brightdata` | Bright Data | `mcp` | `oauth (dynamic)` | |
|
|
123
|
+
| `buildkite` | Buildkite | `mcp` | `oauth (dynamic)` | |
|
|
124
|
+
| `calendly` | Calendly | `mcp` | `oauth (dynamic)` | |
|
|
125
|
+
| `canva` | Canva | `mcp` | `oauth (dynamic)` | |
|
|
126
|
+
| `circleci` | CircleCI | `mcp` | `oauth (dynamic)` | |
|
|
127
|
+
| `clickup` | ClickUp | `mcp` | `oauth (dynamic)` | |
|
|
128
|
+
| `close` | Close | `mcp` | `oauth (dynamic)` | |
|
|
129
|
+
| `cloudflare_bindings` | Cloudflare Bindings | `mcp` | `oauth (dynamic)` | |
|
|
130
|
+
| `cloudflare_observability` | Cloudflare Observability | `mcp` | `oauth (dynamic)` | |
|
|
131
|
+
| `contentful` | Contentful | `mcp` | `oauth (dynamic)` | |
|
|
132
|
+
| `datadog` | Datadog | `mcp` | `oauth (dynamic)` | |
|
|
133
|
+
| `drive_mcp` | Google Drive (Google MCP) | `mcp` | `oauth (manual)` | yes |
|
|
134
|
+
| `dropbox` | Dropbox | `mcp` | `oauth (dynamic)` | |
|
|
135
|
+
| `expensify` | Expensify | `mcp` | `oauth (dynamic)` | |
|
|
136
|
+
| `figma` | Figma | `mcp` | `oauth (dynamic)` | |
|
|
137
|
+
| `fireflies` | Fireflies | `mcp` | `oauth (dynamic)` | |
|
|
138
|
+
| `flagsmith` | Flagsmith | `mcp` | `oauth (dynamic)` | |
|
|
139
|
+
| `gamma` | Gamma | `mcp` | `oauth (dynamic)` | |
|
|
140
|
+
| `github` | GitHub | `mcp` | `bearer` | yes |
|
|
141
|
+
| `gmail_mcp` | Gmail (Google MCP) | `mcp` | `oauth (manual)` | yes |
|
|
142
|
+
| `grafana` | Grafana | `mcp` | `oauth (dynamic)` | |
|
|
143
|
+
| `heroku` | Heroku | `mcp` | `oauth (dynamic)` | |
|
|
144
|
+
| `hubspot` | HubSpot | `mcp` | `oauth (manual)` | |
|
|
145
|
+
| `hygraph` | Hygraph | `mcp` | `oauth (dynamic)` | |
|
|
146
|
+
| `insightly` | Insightly | `mcp` | `oauth (dynamic)` | |
|
|
147
|
+
| `jam` | Jam | `mcp` | `oauth (dynamic)` | |
|
|
148
|
+
| `klaviyo` | Klaviyo | `mcp` | `oauth (dynamic)` | |
|
|
149
|
+
| `linear` | Linear | `mcp` | `oauth (dynamic)` | yes |
|
|
150
|
+
| `mercury` | Mercury | `mcp` | `oauth (dynamic)` | |
|
|
151
|
+
| `miro` | Miro | `mcp` | `oauth (dynamic)` | |
|
|
152
|
+
| `mixpanel` | Mixpanel | `mcp` | `oauth (dynamic)` | |
|
|
153
|
+
| `monday` | monday.com | `mcp` | `oauth (dynamic)` | |
|
|
154
|
+
| `mux` | Mux | `mcp` | `oauth (dynamic)` | |
|
|
155
|
+
| `navan` | Navan | `mcp` | `oauth (dynamic)` | |
|
|
156
|
+
| `neon` | Neon | `mcp` | `oauth (dynamic)` | |
|
|
157
|
+
| `netlify` | Netlify | `mcp` | `oauth (dynamic)` | |
|
|
158
|
+
| `notion` | Notion | `mcp` | `oauth (dynamic)` | yes |
|
|
159
|
+
| `paddle` | Paddle | `mcp` | `oauth (dynamic)` | |
|
|
160
|
+
| `paypal` | PayPal | `mcp` | `oauth (dynamic)` | |
|
|
161
|
+
| `posthog` | PostHog | `mcp` | `oauth (dynamic)` | |
|
|
162
|
+
| `prisma` | Prisma | `mcp` | `oauth (dynamic)` | |
|
|
163
|
+
| `ramp` | Ramp | `mcp` | `oauth (dynamic)` | |
|
|
164
|
+
| `recurly` | Recurly | `mcp` | `oauth (dynamic)` | |
|
|
165
|
+
| `remote` | Remote | `mcp` | `oauth (dynamic)` | |
|
|
166
|
+
| `render` | Render | `mcp` | `bearer` | |
|
|
167
|
+
| `replicate` | Replicate | `mcp` | `oauth (dynamic)` | |
|
|
168
|
+
| `resend` | Resend | `mcp` | `oauth (dynamic)` | |
|
|
169
|
+
| `riverside` | Riverside | `mcp` | `oauth (dynamic)` | |
|
|
170
|
+
| `rootly` | Rootly | `mcp` | `oauth (dynamic)` | |
|
|
171
|
+
| `rudderstack` | RudderStack | `mcp` | `oauth (dynamic)` | |
|
|
172
|
+
| `salesloft` | Salesloft | `mcp` | `oauth (dynamic)` | |
|
|
173
|
+
| `sanity` | Sanity | `mcp` | `oauth (dynamic)` | |
|
|
174
|
+
| `sentry` | Sentry | `mcp` | `oauth (dynamic)` | |
|
|
175
|
+
| `shortcut` | Shortcut | `mcp` | `oauth (dynamic)` | |
|
|
176
|
+
| `slack` | Slack | `mcp` | `oauth (manual)` | yes |
|
|
177
|
+
| `square` | Square | `mcp` | `oauth (dynamic)` | |
|
|
178
|
+
| `storyblok` | Storyblok | `mcp` | `oauth (dynamic)` | |
|
|
179
|
+
| `stripe` | Stripe | `mcp` | `oauth (dynamic)` | |
|
|
180
|
+
| `supabase` | Supabase | `mcp` | `oauth (dynamic)` | |
|
|
181
|
+
| `tavily` | Tavily | `mcp` | `oauth (dynamic)` | |
|
|
182
|
+
| `todoist` | Todoist | `mcp` | `oauth (dynamic)` | |
|
|
183
|
+
| `vercel` | Vercel | `mcp` | `oauth (dynamic)` | |
|
|
184
|
+
| `vimeo` | Vimeo | `mcp` | `oauth (dynamic)` | |
|
|
185
|
+
| `webflow` | Webflow | `mcp` | `oauth (dynamic)` | |
|
|
186
|
+
| `whimsical` | Whimsical | `mcp` | `oauth (dynamic)` | |
|
|
187
|
+
| `wix` | Wix | `mcp` | `oauth (dynamic)` | |
|
|
188
|
+
| `workable` | Workable | `mcp` | `oauth (dynamic)` | |
|
|
189
|
+
| `zapier` | Zapier | `mcp` | `oauth (dynamic)` | |
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Airtable registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const airtable = defineProvider({
|
|
5
|
+
id: 'airtable',
|
|
6
|
+
name: 'Airtable',
|
|
7
|
+
description: 'Bases, tables, records, fields, and schema, via Airtable\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.airtable.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Algolia registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const algolia = defineProvider({
|
|
5
|
+
id: 'algolia',
|
|
6
|
+
name: 'Algolia',
|
|
7
|
+
description: 'Search indices, records, queries, and synonyms, via Algolia\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.algolia.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Amplitude registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const amplitude = defineProvider({
|
|
5
|
+
id: 'amplitude',
|
|
6
|
+
name: 'Amplitude',
|
|
7
|
+
description: 'Events, charts, cohorts, and user activity, via Amplitude\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.amplitude.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Apify registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const apify = defineProvider({
|
|
5
|
+
id: 'apify',
|
|
6
|
+
name: 'Apify',
|
|
7
|
+
description: 'Actors, runs, datasets, and scraped results, via Apify\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.apify.com' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Asana registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const asana = defineProvider({
|
|
5
|
+
id: 'asana',
|
|
6
|
+
name: 'Asana',
|
|
7
|
+
description: 'Tasks, projects, portfolios, and workspaces, via Asana\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.asana.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -40,8 +40,15 @@ import {
|
|
|
40
40
|
* model is the cost the five sources exist to avoid; there is no reason to pay
|
|
41
41
|
* it on the way in that would not also apply on the way out.
|
|
42
42
|
*
|
|
43
|
-
* **
|
|
44
|
-
* rather than a gap.
|
|
43
|
+
* **Neither direction between a mailbox and this store is done here**, and that
|
|
44
|
+
* is a boundary rather than a gap. It cost somebody a real attempt: `store` takes
|
|
45
|
+
* the shared attachment shape, which advertises `message_id`, so an agent asked to
|
|
46
|
+
* keep a PDF that had arrived by mail read the description, named the message, and
|
|
47
|
+
* was refused — twice, having tried the obvious workaround in between. The schema
|
|
48
|
+
* offered a route the handler never had. The description above now says so, and
|
|
49
|
+
* the refusal in `#connectivity/mail` names what does work instead.
|
|
50
|
+
*
|
|
51
|
+
* The reason it cannot is real rather than incidental. A staged handle is scoped to `<provider>/<connection>`
|
|
45
52
|
* (`#dispatch`'s `stageAttachment`), so a handle minted under `assets/main` is
|
|
46
53
|
* deliberately unresolvable from `gmail/main` — bridging the two means crossing
|
|
47
54
|
* the isolation every provider relies on, and that belongs in dispatch if it
|
|
@@ -211,10 +218,10 @@ export const assetsProvider: ProviderDefinition = defineLocalProvider({
|
|
|
211
218
|
name: 'store',
|
|
212
219
|
title: 'Store a file',
|
|
213
220
|
description:
|
|
214
|
-
'Keep a file in this profile. Name exactly one source — path, url, handle,
|
|
221
|
+
'Keep a file in this profile. Name exactly one source — path, url, handle, or data — and the endpoint reads the bytes itself; never base64 a file into this call when any other source will do. Storing under a name that exists replaces it. An attachment sitting in a mailbox cannot be named here: this connection holds no mailbox, so ask the mail connection to send it somewhere it can be reached from.',
|
|
215
222
|
inputSchema: z.object({
|
|
216
223
|
source: attachmentRefSchema.describe(
|
|
217
|
-
'Where the bytes come from. Exactly one of path, url, handle,
|
|
224
|
+
'Where the bytes come from. Exactly one of path, url, handle, or data. The mailbox sources — message_id and uid — are part of the shared shape but cannot resolve here.',
|
|
218
225
|
),
|
|
219
226
|
name: z
|
|
220
227
|
.string()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Jira, Confluence and Compass, through Atlassian's own MCP server.
|
|
5
|
+
*
|
|
6
|
+
* The endpoint is `/v1/mcp/authv2` and the path matters: `/v1/mcp` and
|
|
7
|
+
* `/v1/sse` both answer and neither publishes the protected-resource metadata
|
|
8
|
+
* the SDK discovers from, so a client pointed at either gets a 401 it cannot act
|
|
9
|
+
* on. This one advertises dynamic client registration, which is why there is
|
|
10
|
+
* nothing here for an operator to set up.
|
|
11
|
+
*/
|
|
12
|
+
export const atlassian = defineProvider({
|
|
13
|
+
id: 'atlassian',
|
|
14
|
+
name: 'Atlassian',
|
|
15
|
+
description:
|
|
16
|
+
'Jira issues, Confluence pages, and Compass components, via Atlassian\'s official MCP server.',
|
|
17
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.atlassian.com/v1/mcp/authv2' },
|
|
18
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
19
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Attio registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const attio = defineProvider({
|
|
5
|
+
id: 'attio',
|
|
6
|
+
name: 'Attio',
|
|
7
|
+
description: 'Records, lists, notes, and tasks in the CRM, via Attio\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.attio.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Better Stack registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const betterstack = defineProvider({
|
|
5
|
+
id: 'betterstack',
|
|
6
|
+
name: 'Better Stack',
|
|
7
|
+
description: 'Incidents, monitors, heartbeats, and logs, via Better Stack\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.betterstack.com' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
export const BOX_APP = 'box';
|
|
4
|
+
|
|
5
|
+
/** Matched literally by Box, so it is declared rather than picked at run time. */
|
|
6
|
+
const BOX_REDIRECT_URI = 'http://127.0.0.1:8772/callback';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Box, through the server Box runs.
|
|
10
|
+
*
|
|
11
|
+
* Box publishes protected-resource metadata and an ordinary authorization-code
|
|
12
|
+
* flow, and no dynamic client registration — so the client is the operator's,
|
|
13
|
+
* and there are two consoles it can come from. The admin route is the one Box
|
|
14
|
+
* prefers and the one that works on a managed enterprise account; the developer
|
|
15
|
+
* route is what an individual has. The steps below take the second, and say so.
|
|
16
|
+
*/
|
|
17
|
+
export const box = defineProvider({
|
|
18
|
+
id: 'box',
|
|
19
|
+
name: 'Box',
|
|
20
|
+
description: 'Files, folders, and metadata in Box, via Box\'s official MCP server.',
|
|
21
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.box.com/' },
|
|
22
|
+
auth: {
|
|
23
|
+
kind: 'oauth',
|
|
24
|
+
registration: 'manual',
|
|
25
|
+
app: BOX_APP,
|
|
26
|
+
authorize_url: 'https://account.box.com/api/oauth2/authorize',
|
|
27
|
+
token_url: 'https://api.box.com/oauth2/token',
|
|
28
|
+
redirect_uri: BOX_REDIRECT_URI,
|
|
29
|
+
},
|
|
30
|
+
setup: {
|
|
31
|
+
summary:
|
|
32
|
+
'Box needs an OAuth app of your own. On a managed account an administrator can instead add ' +
|
|
33
|
+
'credentials to the Custom Box MCP Server integration in the Admin Console, which is the route ' +
|
|
34
|
+
'Box prefers; these steps take the developer one, which is what an individual account has.',
|
|
35
|
+
docs_url: 'https://developer.box.com/guides/box-mcp/remote/',
|
|
36
|
+
steps: [
|
|
37
|
+
'Sign in at https://app.box.com/developers/console and choose "Create Platform App" → "Custom App".',
|
|
38
|
+
'Pick "User Authentication (OAuth 2.0)" as the authentication method. Not Server Authentication — that one issues a service identity with no access to your own files.',
|
|
39
|
+
`Under Configuration → OAuth 2.0 Redirect URIs, add exactly: ${BOX_REDIRECT_URI} — the whole URL, including the port. Box matches it literally.`,
|
|
40
|
+
'Under Application Scopes, tick at least "Read all files and folders stored in Box"; add write access only if you want the agent to be able to change files.',
|
|
41
|
+
'Save, then copy the Client ID and Client Secret from the Configuration page.',
|
|
42
|
+
'If your Box account is managed by an administrator, the app also has to be authorised in the Admin Console under Apps → Custom Apps Manager before it will work.',
|
|
43
|
+
],
|
|
44
|
+
troubleshooting:
|
|
45
|
+
'redirect_uri_mismatch means the URI on the app is not the one above — check the port, and 127.0.0.1 rather than localhost. ' +
|
|
46
|
+
'A sign-in that succeeds and then reaches nothing is usually an app awaiting administrator authorisation, or one created with Server Authentication rather than User Authentication.',
|
|
47
|
+
prompts: [
|
|
48
|
+
{
|
|
49
|
+
key: 'client_id',
|
|
50
|
+
label: 'Box app Client ID',
|
|
51
|
+
secret: false,
|
|
52
|
+
scope: 'shared' as const,
|
|
53
|
+
credential_ref: `${BOX_APP}/client_id`,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: 'client_secret',
|
|
57
|
+
label: 'Box app Client Secret',
|
|
58
|
+
secret: true,
|
|
59
|
+
scope: 'shared' as const,
|
|
60
|
+
credential_ref: `${BOX_APP}/client_secret`,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Bright Data registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const brightdata = defineProvider({
|
|
5
|
+
id: 'brightdata',
|
|
6
|
+
name: 'Bright Data',
|
|
7
|
+
description: 'Web scraping, search results, and datasets, via Bright Data\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.brightdata.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Buildkite registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const buildkite = defineProvider({
|
|
5
|
+
id: 'buildkite',
|
|
6
|
+
name: 'Buildkite',
|
|
7
|
+
description: 'Pipelines, builds, jobs, and artifacts, via Buildkite\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.buildkite.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -54,7 +54,7 @@ const manifest = defineProvider({
|
|
|
54
54
|
'bunq issues an API key from inside the app, not from a web console. The key is bound to the IP addresses ' +
|
|
55
55
|
'it is used from unless you mark it as a wildcard key — which you must do in the app if this endpoint will ' +
|
|
56
56
|
'ever run anywhere but this machine.',
|
|
57
|
-
docs: 'docs/
|
|
57
|
+
docs: 'https://lanes.sh/docs/link/bunq',
|
|
58
58
|
docs_url: 'https://doc.bunq.com/basics/authentication/api-keys',
|
|
59
59
|
steps: [
|
|
60
60
|
'In the bunq app: Profile → Security & Settings → Developers → API keys → Add API key.',
|
|
@@ -63,7 +63,7 @@ const manifest = defineProvider({
|
|
|
63
63
|
'If this endpoint will run deployed rather than on this machine, mark the key as a wildcard key in the same screen. bunq refuses to set that over the API, deliberately, so it cannot be done for you.',
|
|
64
64
|
'Copy the key and paste it below. Nothing is sent anywhere until you do — the handshake that registers this device runs immediately afterwards.',
|
|
65
65
|
'You will be asked what to call this connection. bunq has no endpoint that reports whose account a key belongs to, so the label is yours to choose.',
|
|
66
|
-
'To try this without a real account first, use bunq\'s sandbox: https://public-api.sandbox.bunq.com issues a test key and needs no bank account at all. Put a manifest of your own in providers.d/ naming that base_url — see docs/
|
|
66
|
+
'To try this without a real account first, use bunq\'s sandbox: https://public-api.sandbox.bunq.com issues a test key and needs no bank account at all. Put a manifest of your own in providers.d/ naming that base_url — see https://lanes.sh/docs/link/bunq.',
|
|
67
67
|
],
|
|
68
68
|
troubleshooting:
|
|
69
69
|
'bunq refused the key. The usual causes are a key that was revoked in the app, a request from an address the key ' +
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Calendly registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const calendly = defineProvider({
|
|
5
|
+
id: 'calendly',
|
|
6
|
+
name: 'Calendly',
|
|
7
|
+
description: 'Scheduled events, invitees, event types, and availability, via Calendly\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.calendly.com' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** Canva registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const canva = defineProvider({
|
|
5
|
+
id: 'canva',
|
|
6
|
+
name: 'Canva',
|
|
7
|
+
description: 'Designs, folders, brand templates, assets, and exports, via Canva\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.canva.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** CircleCI registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const circleci = defineProvider({
|
|
5
|
+
id: 'circleci',
|
|
6
|
+
name: 'CircleCI',
|
|
7
|
+
description: 'Pipelines, workflows, jobs, and test results, via CircleCI\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.circleci.com/v1/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineProvider } from '#connectivity';
|
|
2
|
+
|
|
3
|
+
/** ClickUp registers us at connect time — nothing for an operator to set up. */
|
|
4
|
+
export const clickup = defineProvider({
|
|
5
|
+
id: 'clickup',
|
|
6
|
+
name: 'ClickUp',
|
|
7
|
+
description: 'Tasks, lists, spaces, docs, and time entries, via ClickUp\'s official MCP server.',
|
|
8
|
+
connector: { kind: 'mcp', endpoint: 'https://mcp.clickup.com/mcp' },
|
|
9
|
+
auth: { kind: 'oauth', registration: 'dynamic' },
|
|
10
|
+
});
|