Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/landing/src/shared/icons/brands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,26 @@ function HermesIcon({ size, ...props }: IconSvgProps) {
return <HermesAgentIcon {...props} defaultLabel="Hermes Agent" size={size} />;
}

function JamIcon({ size, ...props }: IconSvgProps) {
return (
<SvgIcon
{...props}
defaultLabel="Jam"
fill="none"
size={size}
viewBox="0 0 24 24"
>
<path
d="M15 10.5 19.553 8A1 1 0 0 1 21 8.894v6.212A1 1 0 0 1 19.553 16L15 13.5v2A2.5 2.5 0 0 1 12.5 18h-7A2.5 2.5 0 0 1 3 15.5v-7A2.5 2.5 0 0 1 5.5 6h7A2.5 2.5 0 0 1 15 8.5v2Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
/>
</SvgIcon>
);
}

function CodexAppServerApiIcon(props: IconSvgProps) {
return <CodexIcon {...props} defaultLabel="Codex App Server API" />;
}
Expand Down Expand Up @@ -253,6 +273,7 @@ const BRAND_ICONS = {
granola: GranolaIcon,
hermes: HermesIcon,
homebrew: createSimpleBrandIcon(siHomebrew),
jam: JamIcon,
jira: createSimpleBrandIcon(siJira),
laminar: LaminarIcon,
linear: createSimpleBrandIcon(siLinear),
Expand Down
32 changes: 22 additions & 10 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@hono/node-server": "2.0.1",
"@hono/structured-logger": "0.1.0",
"@hono/zod-validator": "0.7.6",
"@modelcontextprotocol/sdk": "1.27.1",
"@nanostores/react": "1.1.0",
"@tabler/icons-react": "3.36.1",
"@tanstack/react-query": "5.90.20",
Expand Down
1 change: 1 addition & 0 deletions packages/db/src/__snapshots__/credentials.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports[`credentials schemas > credentialSchemaMap > matches supported provider
"google_search_console",
"granola",
"hermes",
"jam",
"jira",
"laminar",
"linear",
Expand Down
9 changes: 9 additions & 0 deletions packages/db/src/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,13 @@ export const ConfluenceCredentialsSchema = z.object({

export type ConfluenceCredentials = z.infer<typeof ConfluenceCredentialsSchema>;

export const JamCredentialsSchema = z.object({
accessToken: requiredOpaqueString("Personal access token is required"),
type: z.literal("jam"),
});

export type JamCredentials = z.infer<typeof JamCredentialsSchema>;

export const AmazonAdsCredentialsSchema = z.object({
accessToken: requiredOpaqueString("Access token is required"),
apiBaseUrl: optionalTrimmedUrl("API base URL must be a valid URL"),
Expand Down Expand Up @@ -704,6 +711,7 @@ export const CredentialsSchema = z.union([
GranolaCredentialsSchema,
GoogleSearchConsoleCredentialsSchema,
ConfluenceCredentialsSchema,
JamCredentialsSchema,
AmazonAdsCredentialsSchema,
LinkedInAdsCredentialsSchema,
TikTokMarketingCredentialsSchema,
Expand Down Expand Up @@ -776,6 +784,7 @@ export const credentialSchemaMap = {
google_search_console: GoogleSearchConsoleCredentialsSchema,
granola: GranolaCredentialsSchema,
hermes: HermesCredentialsSchema,
jam: JamCredentialsSchema,
jira: JiraCredentialsSchema,
linkedin_ads: LinkedInAdsCredentialsSchema,
cloudflare_workers_observability:
Expand Down
37 changes: 37 additions & 0 deletions packages/db/src/jam-credentials.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";

import { JamCredentialsSchema } from "./credentials";
import { safeParseSourceProviderCredentials } from "./source-providers";

describe("Jam credentials", () => {
it("accepts a non-empty personal access token", () => {
expect(
JamCredentialsSchema.safeParse({
accessToken: "jam_pat_secret",
type: "jam",
}).success
).toBe(true);
});

it("rejects an empty personal access token", () => {
expect(
JamCredentialsSchema.safeParse({ accessToken: " ", type: "jam" })
.success
).toBe(false);
});

it("injects the Jam credential discriminator from the provider", () => {
const result = safeParseSourceProviderCredentials({
credentials: { accessToken: "jam_pat_secret" },
provider: "jam",
});

expect(result).toMatchObject({
data: {
credentials: { accessToken: "jam_pat_secret", type: "jam" },
provider: "jam",
},
success: true,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ exports[`data-sources schema > matches provider type snapshots 1`] = `
"granola",
"google_search_console",
"confluence",
"jam",
"amazon_ads",
"linkedin_ads",
"tiktok_marketing",
Expand Down Expand Up @@ -70,6 +71,7 @@ exports[`data-sources schema > matches provider type snapshots 1`] = `
"granola",
"google_search_console",
"confluence",
"jam",
"amazon_ads",
"linkedin_ads",
"tiktok_marketing",
Expand Down
29 changes: 29 additions & 0 deletions packages/db/src/source-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
GoogleAnalyticsCredentialsSchema,
GranolaCredentialsSchema,
HermesCredentialsSchema,
JamCredentialsSchema,
JiraCredentialsSchema,
LaminarCredentialsSchema,
LinkedInAdsCredentialsSchema,
Expand Down Expand Up @@ -915,6 +916,34 @@ export const SOURCE_PROVIDER_REGISTRY = {
},
},
},
jam: {
label: "Jam",
credentialSchema: JamCredentialsSchema,
credentialType: "jam",
connectable: true,
analysisSource: true,
queryInterface: false,
sourceApiInterface: true,
testable: true,
dashboardConnectable: true,
dashboardCredentialForm: "jam",
publicCategory: "Developer workflow",
guide: {
summary:
"Connect a Jam workspace through Jam MCP with a read-only personal access token.",
steps: [
"Open Jam workspace Settings, choose MCP, and create a personal access token for this connection.",
"Grant only the `mcp:read` scope so OneQuery can inspect Jams without moving them or adding comments.",
"Copy the token immediately and store it in `credentials.accessToken`; Jam does not show the token again after creation.",
],
exampleInput: {
sourceKey: "jam_workspace",
credentials: {
accessToken: "jam_pat_...",
},
},
},
},
amazon_ads: {
label: "Amazon Ads",
credentialSchema: AmazonAdsCredentialsSchema,
Expand Down
1 change: 1 addition & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@bufbuild/protovalidate": "catalog:",
"@hono/structured-logger": "catalog:",
"@hono/zod-validator": "catalog:",
"@modelcontextprotocol/sdk": "catalog:",
"@noble/ciphers": "2.1.1",
"@onequery/audit-contracts": "workspace:*",
"@onequery/base": "workspace:*",
Expand Down
6 changes: 6 additions & 0 deletions packages/server/src/services/data-source-tester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { testAmplitudeConnection } from "./testers/amplitude-tester";
import { DEFAULT_CONNECTION_TEST_TIMEOUT_SECONDS } from "./testers/defaults";
import { testGoogleAnalyticsConnection } from "./testers/ga-tester";
import { testGitHubConnection } from "./testers/github-tester";
import { testJamConnection } from "./testers/jam-tester";
import { testMixpanelConnection } from "./testers/mixpanel-tester";
import { testPostHogConnection } from "./testers/posthog-tester";
import { testSentryConnection } from "./testers/sentry-tester";
Expand Down Expand Up @@ -49,6 +50,11 @@ const DIRECT_CONNECTION_TESTERS: Partial<
testGitHubConnection(
credentials as Extract<Credentials, { type: "github" }>
),
jam: async (credentials, options): Promise<DataSourceTestOutcome> =>
testJamConnection(
credentials as Extract<Credentials, { type: "jam" }>,
options.timeoutSeconds
),
mixpanel: async (credentials, options): Promise<DataSourceTestOutcome> =>
testMixpanelConnection(
credentials as Extract<Credentials, { type: "mixpanel" }>,
Expand Down
Loading