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
356 changes: 178 additions & 178 deletions bun.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion content-scraper/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"build": "bun run build:server"
},
"dependencies": {
"@decocms/runtime": "^1.1.2",
"@decocms/runtime": "^1.1.3",
"@supabase/supabase-js": "^2.49.0",
"zod": "^4.0.0"
},
Expand Down
1 change: 1 addition & 0 deletions deco-llm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.dev.vars
11 changes: 11 additions & 0 deletions deco-llm/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"scopeName": "deco",
"name": "llm",
"connection": {
"type": "HTTP",
"url": "https://sites-deco-llm.decocache.com/mcp"
},
"description": "Deco LLM App Connection for LLM uses.",
"icon": "https://assets.decocache.com/mcp/6e1418f7-c962-406b-aceb-137197902709/ai-gateway.png",
"unlisted": false
}
40 changes: 40 additions & 0 deletions deco-llm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "deco-llm",
"version": "1.0.0",
"description": "Deco LLM App Connection for LLM uses.",
"private": true,
"type": "module",
"scripts": {
"dev": "bun run --hot server/main.ts",
"build:server": "NODE_ENV=production bun build server/main.ts --target=bun --outfile=dist/server/main.js",
"build": "bun run build:server",
"publish": "cat app.json | deco registry publish -w /shared/deco -y",
"check": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/provider": "^3.0.2",
"@ai-sdk/provider-utils": "^4.0.4",
"@decocms/bindings": "^1.0.7",
"@decocms/runtime": "^1.1.3",
"@openrouter/ai-sdk-provider": "^1.5.4",
"@openrouter/sdk": "^0.3.11",
"ai": "^6.0.3",
"zod": "^4.0.0"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.13.4",
"@cloudflare/workers-types": "^4.20251014.0",
"@decocms/mcps-shared": "1.0.0",
"@decocms/openrouter": "1.0.0",
"@mastra/core": "^0.24.0",
"@modelcontextprotocol/sdk": "1.25.1",
"@types/mime-db": "^1.43.6",
"deco-cli": "^0.28.0",
"typescript": "^5.7.2",
"vite": "7.2.0",
"wrangler": "^4.28.0"
},
"engines": {
"node": ">=22.0.0"
}
}
104 changes: 104 additions & 0 deletions deco-llm/server/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* OpenRouter MCP Server
*
* This MCP provides tools for interacting with OpenRouter's API,
* including model discovery, comparison, and AI chat completions.
*
* OpenRouter offers a unified API for accessing hundreds of AI models
* with built-in fallback mechanisms, cost optimization, and provider routing.
*/
import type { Registry } from "@decocms/mcps-shared/registry";
import { serve } from "@decocms/mcps-shared/serve";
import { tools } from "@decocms/openrouter/tools";
import { BindingOf, type DefaultEnv, withRuntime } from "@decocms/runtime";
import { z } from "zod";
import { calculatePreAuthAmount, toMicrodollars } from "./usage";

export const StateSchema = z.object({
WALLET: BindingOf("@deco/wallet"),
});

/**
* Environment type combining Deco bindings and Cloudflare Workers context
*/
export type Env = DefaultEnv<typeof StateSchema, Registry>;

interface OpenRouterUsageReport {
providerMetadata: {
openrouter: {
usage: {
cost: number;
};
};
};
}
const isOpenRouterUsageReport = (
usage: unknown | OpenRouterUsageReport,
): usage is OpenRouterUsageReport => {
return (
typeof usage === "object" &&
usage !== null &&
"providerMetadata" in usage &&
typeof usage.providerMetadata === "object" &&
usage.providerMetadata !== null &&
"openrouter" in usage.providerMetadata &&
typeof usage.providerMetadata.openrouter === "object" &&
usage.providerMetadata.openrouter !== null &&
"usage" in usage.providerMetadata.openrouter &&
typeof usage.providerMetadata.openrouter.usage === "object" &&
usage.providerMetadata.openrouter.usage !== null &&
"cost" in usage.providerMetadata.openrouter.usage
);
};

const runtime = withRuntime<
DefaultEnv<typeof StateSchema, Registry>,
typeof StateSchema,
Registry
>({
tools: (env) => {
return tools(env, {
start: async (modelInfo, params) => {
const amount = calculatePreAuthAmount(modelInfo, params);

const { id } =
await env.MESH_REQUEST_CONTEXT.state.WALLET.PRE_AUTHORIZE_AMOUNT({
amount,
metadata: {
modelId: modelInfo.id,
params: params,
},
});
return {
end: async (usage) => {
if (!isOpenRouterUsageReport(usage)) {
throw new Error("Usage cost not found");
}
const vendorId = process.env.WALLET_VENDOR_ID ?? "deco";
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: process.env is not available in Cloudflare Workers environment. The WALLET_VENDOR_ID environment variable will always be undefined, causing the code to always fall back to "deco". Consider accessing environment variables through the Deco runtime's env parameter instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At deco-llm/server/main.ts, line 77:

<comment>`process.env` is not available in Cloudflare Workers environment. The `WALLET_VENDOR_ID` environment variable will always be undefined, causing the code to always fall back to `"deco"`. Consider accessing environment variables through the Deco runtime's `env` parameter instead.</comment>

<file context>
@@ -0,0 +1,104 @@
+            if (!isOpenRouterUsageReport(usage)) {
+              throw new Error("Usage cost not found");
+            }
+            const vendorId = process.env.WALLET_VENDOR_ID ?? "deco";
+            await env.MESH_REQUEST_CONTEXT.state.WALLET.COMMIT_PRE_AUTHORIZED_AMOUNT(
+              {
</file context>
Fix with Cubic

await env.MESH_REQUEST_CONTEXT.state.WALLET.COMMIT_PRE_AUTHORIZED_AMOUNT(
{
identifier: id,
contractId:
env.MESH_REQUEST_CONTEXT.connectionId ??
env.MESH_REQUEST_CONTEXT.state.WALLET.value,
vendorId,
amount: toMicrodollars(
usage.providerMetadata.openrouter.usage.cost,
),
},
);
},
};
},
});
},
configuration: {
state: StateSchema,
scopes: [
"WALLET::PRE_AUTHORIZE_AMOUNT",
"WALLET::COMMIT_PRE_AUTHORIZED_AMOUNT",
],
},
});

serve(runtime.fetch);
44 changes: 44 additions & 0 deletions deco-llm/server/usage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { LanguageModelInputSchema } from "@decocms/bindings/llm";
import type { ModelInfo } from "@decocms/openrouter/types";
import type { z } from "zod";

export interface GenerationContext {
model: ModelInfo;
}

const DEFAULT_MAX_COMPLETION_TOKENS = 1000000;

export const toMicrodollars = (amount: number): string => {
return Math.round(amount * 1_000_000).toString();
};
/**
*
* @param model - The model to calculate the pre-auth amount for
* @param params - The parameters for the language model
* @returns The pre-auth amount in microdollars
*/
export const calculatePreAuthAmount = (
model: ModelInfo,
params: z.infer<typeof LanguageModelInputSchema>,
): string => {
const maxContextLength = Math.min(
JSON.stringify({
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Unit mismatch: maxContextLength is calculated as character count (via JSON.stringify().length), but it's multiplied by a per-token price. Characters and tokens are different units. Consider using a tokenizer to estimate token count, or apply a character-to-token ratio approximation (e.g., divide by 4 for rough estimate).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At deco-llm/server/usage.ts, line 25:

<comment>Unit mismatch: `maxContextLength` is calculated as character count (via `JSON.stringify().length`), but it's multiplied by a per-token price. Characters and tokens are different units. Consider using a tokenizer to estimate token count, or apply a character-to-token ratio approximation (e.g., divide by 4 for rough estimate).</comment>

<file context>
@@ -0,0 +1,44 @@
+  params: z.infer<typeof LanguageModelInputSchema>,
+): string => {
+  const maxContextLength = Math.min(
+    JSON.stringify({
+      ...params.callOptions.prompt,
+      ...params.callOptions.tools,
</file context>
Fix with Cubic

...params.callOptions.prompt,
...params.callOptions.tools,
}).length,
model.context_length,
);

const maxCompletionTokens =
params.callOptions.maxOutputTokens ??
model.top_provider?.max_completion_tokens ??
DEFAULT_MAX_COMPLETION_TOKENS;

const constPerCompletionToken = parseFloat(model.pricing.completion);
const constPerPromptToken = parseFloat(model.pricing.prompt);

const amountUsd =
maxContextLength * constPerPromptToken +
maxCompletionTokens * constPerCompletionToken;
return toMicrodollars(amountUsd);
};
42 changes: 42 additions & 0 deletions deco-llm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2023", "ES2024", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"verbatimModuleSyntax": false,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
"allowJs": true,

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,

/* Path Aliases */
"baseUrl": ".",
"paths": {
"shared/*": ["./shared/*"],
"server/*": ["./server/*"],
"worker/*": ["./worker/*"]
},

/* Types */
"types": ["@cloudflare/workers-types"]
},
"include": [
"server",
"shared",
"vite.config.ts"
]
}
2 changes: 1 addition & 1 deletion google-calendar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"check": "tsc --noEmit"
},
"dependencies": {
"@decocms/runtime": "^1.1.2",
"@decocms/runtime": "^1.1.3",
"zod": "^4.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion google-tag-manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"check": "tsc --noEmit"
},
"dependencies": {
"@decocms/runtime": "^1.1.2",
"@decocms/runtime": "^1.1.3",
"zod": "^4.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion mcp-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"dependencies": {
"@ai-sdk/mcp": "^1.0.1",
"@decocms/bindings": "^1.0.8",
"@decocms/runtime": "^1.1.2",
"@decocms/runtime": "^1.1.3",
"@jitl/quickjs-singlefile-cjs-release-sync": "^0.31.0",
"@modelcontextprotocol/sdk": "^1.25.1",
"@radix-ui/react-collapsible": "^1.1.12",
Expand Down
80 changes: 3 additions & 77 deletions mcp-studio/server/types/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@
* Central definition for the Env type used throughout the workflow system.
*/

import type { EVENT_BUS_BINDING } from "@decocms/bindings";
import type { createCollectionBindings } from "@decocms/bindings/collections";
import {
BindingOf,
type BindingRegistry,
type DefaultEnv,
} from "@decocms/runtime";
import type { Registry } from "@decocms/mcps-shared/registry";
import { BindingOf, type DefaultEnv } from "@decocms/runtime";
import z from "zod";

export const StateSchema = z.object({
Expand All @@ -19,74 +14,5 @@ export const StateSchema = z.object({
CONNECTION: BindingOf("@deco/connection"),
});

export type ConnectionBinding = {
COLLECTION_CONNECTIONS_UPDATE: (params: {
id: string;
data: {
configuration_state: object;
configuration_scopes: string[];
};
}) => Promise<unknown>;
COLLECTION_CONNECTIONS_GET: (params: { id: string }) => Promise<{
item: {
configuration_state: object;
configuration_scopes: string[];
tools: {
name: string;
description: string;
inputSchema: object;
outputSchema: object;
}[];
};
}>;
// Accepts an (empty) object because MCP tool validation rejects `undefined` inputs.
COLLECTION_CONNECTIONS_LIST: (params?: Record<string, never>) => Promise<{
items: {
id: string;
title: string;
tools: {
name: string;
description: string;
inputSchema: object;
outputSchema: object;
}[];
}[];
}>;
};
const ConnectionSchema = z.object({
configuration_state: z.object({}),
configuration_scopes: z.array(z.string()),
tools: z.array(
z.object({
name: z.string(),
description: z.string(),
inputSchema: z.object({}),
outputSchema: z.object({}),
}),
),
});

export interface Registry extends BindingRegistry {
"@deco/event-bus": typeof EVENT_BUS_BINDING;
"@deco/connection": ReturnType<
typeof createCollectionBindings<typeof ConnectionSchema, "connections">
>;
"@deco/postgres": [
{
name: "DATABASES_RUN_SQL";
description: "Run a SQL query against the database";
inputSchema: z.ZodType<{
sql: string;
params?: unknown[];
}>;
outputSchema: z.ZodType<{
result: {
results?: unknown[];
success?: boolean;
}[];
}>;
},
];
}

export type Env = DefaultEnv<typeof StateSchema, Registry>;
export type { Registry };
2 changes: 1 addition & 1 deletion meta-ads/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dev:tunnel": "deco link -p 3003 -- PORT=3003 bun run dev"
},
"dependencies": {
"@decocms/runtime": "^1.1.2",
"@decocms/runtime": "^1.1.3",
"zod": "^4.0.0"
},
"devDependencies": {
Expand Down
Loading
Loading