@cursor-ai/january
v0.2.1
Published
Experimental project
Keywords
Readme
@anysphere/cursor-sdk
Public SDK for Cursor Agent APIs.
Agent SDK
The Agent SDK provides stable, backwards-compatible types for working with Cursor Agent messages, tool calls, and results.
Installation
import {
UserMessage,
ToolCall,
Message,
} from "@anysphere/cursor-sdk/agent-sdk";Message Types
- UserMessage: Messages from users to the agent
- AssistantMessage: Messages from the AI assistant
- ToolCall: Tool invocations made by the agent
- ToolResult: Results from tool executions
All messages include a session_id field to group related messages in a conversation.
Example
import { Message, isToolCall } from "@anysphere/cursor-sdk/agent-sdk";
const message: Message = {
type: "tool_call",
session_id: "abc123",
name: "read_file",
input: { path: "/path/to/file.ts" },
};
if (isToolCall(message)) {
console.log(`Calling tool: ${message.name}`);
}Tool Names
The SDK supports two categories of tools:
First-Party Tools
The SDK maps internal tool names to public-friendly names:
readToolCall→read_fileshellToolCall→run_terminal_cmdgrepToolCall→grep- etc.
Use normalizeToolName() to convert proto tool names to public API names.
MCP Tools
MCP (Model Context Protocol) tools follow the naming convention mcp__provider__tool:
mcp__filesystem__read_file- File system provider's read_file toolmcp__database__query_table- Database provider's query_table toolmcp__github__create_issue- GitHub provider's create_issue tool
Working with MCP Tools
import {
isMcpTool,
parseMcpToolName,
createMcpToolName,
McpToolInput,
McpToolResult,
} from "@anysphere/cursor-sdk/agent-sdk";
// Check if a tool is an MCP tool
const toolName = "mcp__github__create_issue";
if (isMcpTool(toolName)) {
console.log("This is an MCP tool");
}
// Parse MCP tool name
const parsed = parseMcpToolName(toolName);
if (parsed) {
console.log(`Provider: ${parsed.provider}, Tool: ${parsed.tool}`);
// Output: Provider: github, Tool: create_issue
}
// Create MCP tool name
const mcpToolName = createMcpToolName("database", "query_table");
console.log(mcpToolName); // mcp__database__query_table
// MCP tool call example
const mcpToolCall: Message = {
type: "tool_call",
session_id: "abc123",
name: "mcp__github__create_issue",
input: {
title: "Bug report",
body: "Found an issue...",
labels: ["bug", "high-priority"],
},
};MCP Tool Input/Output
MCP tools use flexible input/output schemas:
- Input: Any JSON-serializable object (
McpToolInput) - Output: MCP-compliant result format with content array (
McpToolResult)
import { McpToolResult } from "@anysphere/cursor-sdk/agent-sdk";
const mcpResult: McpToolResult = {
content: [
{
type: "text",
text: "Issue #123 created successfully",
},
{
type: "resource",
resource: {
uri: "https://github.com/owner/repo/issues/123",
text: "View the created issue",
mimeType: "text/html",
},
},
],
isError: false,
};Streaming Delta Types
For streaming interactions, the SDK exposes delta/update types mirroring internal interaction updates:
import type {
InteractionUpdate,
TextDeltaUpdate,
ThinkingDeltaUpdate,
ToolCallStartedUpdate,
ToolCallCompletedUpdate,
SummaryStartedUpdate,
SummaryCompletedUpdate,
ShellOutputDeltaUpdate,
InteractionListener,
} from "@anysphere/cursor-sdk/agent-sdk";
function handleUpdate(update: InteractionUpdate) {
switch (update.type) {
case "text-delta": {
// handle streaming assistant text
break;
}
case "tool-call-started": {
// update UI with pending tool call
break;
}
case "tool-call-completed": {
// display tool result
break;
}
case "shell-output-delta": {
// stream shell output events
break;
}
}
}Simple Agent SDK (MVP)
import { CursorAgent, type WorkingLocation } from "@anysphere/cursor-sdk/agent";
const workingLocation: WorkingLocation =
process.env.REPO_URL
? { type: "github", repository: process.env.REPO_URL!, ref: process.env.REPO_REF ?? "main" }
: { type: "local", localDirectory: process.cwd() };
const agent = new CursorAgent({ apiKey: process.env.CURSOR_API_KEY!, model: "gpt-4o", workingLocation });
// Option 1: Stream the deltas in real-time
const { stream, conversation } = agent.submit({
message: "Refactor the utils module",
});
for await (const delta of stream) {
// Handle InteractionUpdate deltas
}
const turns = await conversation; // Resolves after stream is consumed
// Option 2: Use callbacks for step-by-step updates
const result = agent.submit({
message: "Refactor the utils module",
onStep: ({ step }) => {
// Called when each step completes (assistant text, tool calls, thinking)
console.log("Step completed:", step);
},
onDelta: ({ update }) => {
// Called on every delta (text-delta, tool-call-completed, etc.)
console.log("Delta:", update.type);
},
});
// Option 3: Just wait for the conversation to complete
const turns = await agent.submit({ message: "..." }).conversation;
// Option 4: Inspect conversation at any time (returns shallow copy)
const currentConversation = agent.conversation();
// Notes
// - When using { type: "github" }, the SDK creates a cloud agent and immediately
// streams updates via the public SSE endpoint (`/v0/agents/:id/conversation/stream`).
// - Abort for cloud agents is not supported and will throw a NotSupported error.
// - Follow-up messages require a separate endpoint; see the Cloud Agent API docs:
// https://cursor.com/docs/background-agent/api/endpoints