-
Notifications
You must be signed in to change notification settings - Fork 0
feat: initialize StackOne TypeScript agent with configs and examples #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Introduces a new stackone-typescript-agent example app that uses the Vercel AI SDK + Anthropic to dynamically discover StackOne MCP tools and run an interactive CLI agent.
Changes:
- Added a new TypeScript app with scripts, TS config, and environment variable example.
- Implemented an interactive CLI agent that discovers linked accounts, lists MCP tools per account, and enables direct tool invocation via
generateText. - Added dynamic system prompt and example queries based on connected providers.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| apps/stackone-typescript-agent/src/agent.ts | Implements MCP account/tool discovery, tool execution wiring, prompt building, and CLI loop. |
| apps/stackone-typescript-agent/package.json | Adds dependencies and scripts for running/typechecking the agent. |
| apps/stackone-typescript-agent/tsconfig.json | TypeScript compiler configuration for the new app. |
| apps/stackone-typescript-agent/.env.example | Documents required environment variables for running the agent. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function createVercelTool(mcpTool: McpTool): CoreTool { | ||
| const toolName = mcpTool.name; | ||
|
|
||
| return { | ||
| description: mcpTool.description || `Call the ${toolName} tool`, | ||
| parameters: z.record(z.unknown()).describe("Arguments for the tool"), | ||
| execute: async (args: Record<string, unknown>) => { |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
McpTool includes inputSchema, but createVercelTool currently uses a generic z.record(z.unknown()) and ignores the provided schema. This loses parameter shape information and removes any validation/guidance for the model. Consider translating inputSchema into a Zod schema when available (and falling back to a permissive schema only when it’s missing).
| toolToAccount.set(mcpTool.name, account.id); | ||
| tools[mcpTool.name] = createVercelTool(mcpTool); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tools[mcpTool.name] and toolToAccount.set(mcpTool.name, …) will silently overwrite when multiple linked accounts expose the same MCP tool name (e.g., two Gmail accounts). That can route tool calls to the wrong account. Consider namespacing tool names (e.g., include account id/provider in the exposed tool name) or storing multiple accounts per tool and requiring disambiguation.
| toolToAccount.set(mcpTool.name, account.id); | |
| tools[mcpTool.name] = createVercelTool(mcpTool); | |
| // Ensure tool names are unique across accounts to avoid silent overwrites. | |
| const baseName = mcpTool.name; | |
| let uniqueToolName = baseName; | |
| if (tools[uniqueToolName]) { | |
| // First try namespacing by provider. | |
| uniqueToolName = `${account.provider}_${baseName}`; | |
| } | |
| if (tools[uniqueToolName]) { | |
| // If still colliding (e.g., multiple accounts with same provider), | |
| // append a numeric suffix until we find an available name. | |
| let counter = 2; | |
| let candidate = `${uniqueToolName}_${counter}`; | |
| while (tools[candidate]) { | |
| counter += 1; | |
| candidate = `${uniqueToolName}_${counter}`; | |
| } | |
| uniqueToolName = candidate; | |
| } | |
| toolToAccount.set(uniqueToolName, account.id); | |
| tools[uniqueToolName] = createVercelTool(mcpTool); |
| tools[mcpTool.name] = createVercelTool(mcpTool); | ||
| } | ||
|
|
||
| providerCounts[account.provider] = mcpTools.length; |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
providerCounts[account.provider] = mcpTools.length overwrites counts when there are multiple active accounts for the same provider; the summary and system prompt will be inaccurate. Accumulate counts per provider (e.g., providerCounts[provider] = (providerCounts[provider] ?? 0) + mcpTools.length).
| providerCounts[account.provider] = mcpTools.length; | |
| providerCounts[account.provider] = | |
| (providerCounts[account.provider] ?? 0) + mcpTools.length; |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 issues found across 4 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/stackone-typescript-agent/src/agent.ts">
<violation number="1" location="apps/stackone-typescript-agent/src/agent.ts:105">
P2: Using `||` here discards valid falsy tool results (0/""/false) and returns the wrapper object instead. Use nullish coalescing to preserve legitimate results.</violation>
<violation number="2" location="apps/stackone-typescript-agent/src/agent.ts:174">
P2: Tools from multiple linked accounts can overwrite each other because they’re keyed only by tool name. For users with multiple accounts on the same provider, the last account silently wins and tool calls may be routed to the wrong account.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| accountId | ||
| )) as { result?: unknown }; | ||
|
|
||
| return result.result || result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Using || here discards valid falsy tool results (0/""/false) and returns the wrapper object instead. Use nullish coalescing to preserve legitimate results.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/stackone-typescript-agent/src/agent.ts, line 105:
<comment>Using `||` here discards valid falsy tool results (0/""/false) and returns the wrapper object instead. Use nullish coalescing to preserve legitimate results.</comment>
<file context>
@@ -0,0 +1,400 @@
+ accountId
+ )) as { result?: unknown };
+
+ return result.result || result;
+}
+
</file context>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name=".gitignore">
<violation number="1" location=".gitignore:40">
P2: Avoid ignoring lock files globally. Lockfiles should be committed so installs are reproducible across machines and CI.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| headers: { | ||
| Authorization: `Basic ${Buffer.from(STACKONE_API_KEY + ":").toString("base64")}`, | ||
| "x-account-id": accountId, | ||
| "Content-Type": "application/json", | ||
| Accept: "application/json, text/event-stream", | ||
| }, | ||
| body: JSON.stringify({ | ||
| jsonrpc: "2.0", | ||
| id: `req-${method}-${Date.now()}`, |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The request advertises Accept: application/json, text/event-stream but the response is always parsed via response.json(). If the server chooses text/event-stream (SSE), this will fail at runtime. Either request only JSON here or add handling for SSE responses.
| @@ -0,0 +1,22 @@ | |||
| { | |||
| "name": "stackone-typescript-agent", | |||
| "version": "1.0.0", | |||
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This app's package.json is missing "private": true. Other example apps in this repo (e.g. apps/oauth-redirect-proxy/package.json) mark themselves private to prevent accidental publication to npm.
| "version": "1.0.0", | |
| "version": "1.0.0", | |
| "private": true, |
| "scripts": { | ||
| "agent": "tsx src/agent.ts", | ||
| "typecheck": "tsc --noEmit" | ||
| }, |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This app defines a typecheck script, but the repo's root uses turbo run check-types (see turbo.json + root package.json). As-is, this app won't participate in the standard type-check task. Rename/add a check-types script (and optionally keep typecheck as an alias) to integrate with the monorepo workflows.
| // For mixed enums, use union of literals | ||
| const literals = schema.enum.map((v) => z.literal(v as string | number | boolean)); | ||
| const zodUnion = z.union(literals as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]]); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For non-string enums, this builds a z.union(...) from schema.enum. If the enum has only 1 value (e.g., [1] or [null]), z.union will throw because it requires at least 2 options. Handle single-value enums by returning z.literal(value) (and include null as a supported literal type).
| // For mixed enums, use union of literals | |
| const literals = schema.enum.map((v) => z.literal(v as string | number | boolean)); | |
| const zodUnion = z.union(literals as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]]); | |
| // For mixed enums, use literals; handle single-value enums without z.union | |
| const literals = schema.enum.map((v) => | |
| z.literal(v as string | number | boolean | null), | |
| ); | |
| if (literals.length === 1) { | |
| const singleLiteral = literals[0]; | |
| return schema.description | |
| ? singleLiteral.describe(schema.description) | |
| : singleLiteral; | |
| } | |
| const zodUnion = z.union( | |
| literals as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]], | |
| ); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…age-lock.json for stackone-typescript-agent
This pull request introduces a new TypeScript-based StackOne AI Agent example application that uses the Vercel AI SDK and supports dynamic tool creation from StackOne's MCP endpoint. The agent allows direct tool invocation (without a meta-tool) and features an interactive CLI for user interaction. The implementation includes configuration, dependencies, agent logic, and TypeScript project setup.
The most important changes are:
New Application: StackOne TypeScript Agent
stackone-typescript-agentapp, including configuration files (.env.example,package.json,tsconfig.json) and a complete agent implementation insrc/agent.ts. [1] [2] [3] [4]Agent Implementation and Features (
src/agent.ts):Project Configuration and Dependencies:
.env.examplefor required environment variables, including API keys for StackOne and Anthropic.package.jsonwith necessary dependencies (@ai-sdk/anthropic,ai,dotenv,zod, etc.) and scripts for running and type-checking the agent.tsconfig.jsonfor strict typing, ES module support, and output settings.Summary by cubic
Adds a TypeScript StackOne agent example that discovers tools from MCP for each linked account and calls them directly via the Vercel SDK. Includes an interactive CLI with history and a provider-aware system prompt.
New Features
Dependencies
Written for commit fa587dc. Summary will update on new commits.