The artinet SDK is a Full Stack Agent builder written in TypeScript for node.js that aims to simplify the creation of interoperable AI agents. Learn more at the artinet project.
This SDK significantly supports multiple agentic communication protocols, offering a production-ready solution with a focus on developer experience, reliability, and comprehensive features.
To build your own agent/server use the create-quick-agent
command:
npx @artinet/create-quick-agent@latest
Its got serveral template projects that you can use to get started building agents today.
- artinet SDK
- Plug-and-Play Server: Built on Express.js, the
ExpressServer
handles Transport-layer complexity, routing, protocol compliance, and Server-Sent Events (SSE) streaming mechanics automatically. Just provide your core agent logic (AgentEngine
) and configuration viaExpressServerOptions
. - TypeScript First: Fully written in TypeScript with comprehensive type definitions for a robust developer experience.
- Protocol Compliance: Implements the complete A2A specification with growing support for recieving commands via MCP & ACP.
- Code Deployment (Experimental) | Bundle, test, and deploy agent code onto the artinet. Includes bundler, task wrapper, and deployment utilities.
Component/Feature | Description | Key Classes/Types |
---|---|---|
Client | Interact with A2A-compliant agents. Supports standard & streaming requests. | A2AClient , RpcError |
Server | Host agents that can communicate using a variety of protocols. Handles protocol details, routing, SSE. | ExpressServer , ExpressServerOptions |
Reqeust Handling | Define agent logic using async generators. | AgentEngine , ExecutionContext , UpdateEvent |
Storage | Persist event state. In-memory and file-based options included. | Store , InMemoryTaskStore , FileStore |
Streaming (SSE) | Handle real-time updates via SSE. | |
Logging | Configure structured logging for debugging and monitoring. | logger , configureLogger , LogLevel |
Advanced Server | Customize the underlying JSON-RPC server or integrate into existing apps. | CreateJSONRPCServerParams , createJSONRPCMethod |
Core Types | Based on the official JSON Schemas. | Tool ,AgentCard , Task , Message , Part , Artifact , etc. |
Agent Utilities (for Sandboxed Environments) | Standardized utilities for agents in managed environments to interact with the host system for task lifecycle, inter-agent communication, and external API calls. | artinet.v0.taskManager , artinet.v0.connect , artinet.v0.agent , TaskProxy , ConnectAPICallback , ClientProxy , ClientFactory |
npm install @artinet/sdk
- Node.js (v22.0.0 or higher recommended, check
package.json
engines for exact requirement)
For more detailed documentation visit our documentation site here.
A basic A2A server and client interaction. For more detailed examples, see the examples/
directory.
1. Server (quick-server.ts
)
import {
ExpressServer,
ExecutionContext,
AgentEngine,
InMemoryTaskStore,
} from "@artinet/sdk";
// Minimal agent logic: receive text, yield working state, yield completed state with echo
const quickAgentLogic: AgentEngine = async function* (context: ExecutionContext) {
const params = context.getRequestParams() as MessageSendParams; //for A2A requests
const userInput = params.message.parts[0].kind === "text"
? context.userMessage.parts[0].text
: "";
yield { state: "working" };
// Simulate some work if needed, check context.isCancelled()
yield {
state: "completed",
message: {
role: "agent",
parts: [{ kind: "text", text: `You said: ${userInput}` }],
},
};
};
const server = new A2AServer({
handler: quickAgentLogic,
taskStore: new InMemoryTaskStore(),
port: 4000,
basePath: "/a2a",
card: {
name: "QuickStart Agent",
url: "http://localhost:4000/a2a",
version: "0.1.0",
capabilities: { streaming: true },
skills: [{ id: "echo", name: "Echo Skill" }],
},
});
server.start();
console.log("A2A Server running at http://localhost:4000/a2a");
2. Client (quick-client.ts
)
import { A2AClient, TaskStatusUpdateEvent } from "@artinet/sdk";
async function runClient() {
const client = new A2AClient("http://localhost:4000/a2a");
const message = {
messageId: "test-message-id",
kind: "message",
role: "user",
parts: [{ kind: "text", text: "Hello Quick Start!" }],
};
const stream = client.sendStreamingMessage({ message });
for await (const update of stream) {
// process the update
...
}
console.log("Stream finished.");
}
runClient().catch(console.error);
npm test
To run tests with coverage:
npm run test:coverage
The Artinet SDK is written entirely in TypeScript and includes comprehensive type definitions, providing strong typing and enhanced developer experience.
Interact with A2A-compliant agents using the A2AClient
. See examples/
for more.
Send a message using message/send
.
import { A2AClient, Message } from "@artinet/sdk";
async function runBasicTask() {
const client = new A2AClient("https://your-a2a-server.com/a2a");
const message: Message = {
messageId: "test-message",
kind: "message",
role: "user",
parts: [{ kind: "text", text: "What is the capital of France?" }],
};
const task = await client.sendMessage({ message });
console.log("Task Completed:", task);
}
Receive real-time updates via SSE using message/stream
(recommended).
import {
A2AClient,
Message,
TaskStatusUpdateEvent,
TaskArtifactUpdateEvent,
} from "@artinet/sdk";
async function runStreamingTask() {
const client = new A2AClient("https://your-a2a-server.com/a2a");
const message: Message = {
role: "user",
parts: [{ type: "text", text: "Tell me a short story." }],
};
const stream = client.sendStreamingMessage({ id: "streaming-task-1", message });
for await (const update of stream) {
if ((update as TaskStatusUpdateEvent).status) {
console.log("Status:", (update as TaskStatusUpdateEvent).status.state);
} else if ((update as TaskArtifactUpdateEvent).artifact) {
console.log(
"Artifact:",
(update as TaskArtifactUpdateEvent).artifact.name
);
}
}
console.log("Stream finished.");
}
Add headers using addHeader
or setHeaders
.
import { A2AClient } from "@artinet/sdk";
const client = new A2AClient("https://your-secure-a2a-server.com/a2a");
// Add a single header
client.addHeader("Authorization", "Bearer your-api-token");
// Set multiple headers (overwrites existing)
client.setHeaders({ Authorization: "Bearer ...", "X-Custom": "value" });
Host agents using A2AServer
. Handles protocol details. See examples/
for more.
Define agent behavior with an async generator TaskHandler
.
import {
A2AServer,
ExecutionContext,
AgentEngine,
InMemoryTaskStore,
} from "@artinet/sdk";
const myAgent: AgentEngine = async function* (context: ExecutionContext) {
yield {
state: "working",
message: {
role: "agent",
parts: [{ type: "text", text: "Processing..." }],
},
};
// Check context.isCancelled() if operation is long
// await someAsyncTask();
...
yield {
name: "result.txt",
mimeType: "text/plain",
parts: [{ type: "text", text: "Report data" }],
};
yield {
state: "completed",
message: {
role: "agent",
parts: [{ type: "text", text: "Finished processing." }],
},
};
};
const myServer = new A2AServer({
handler: myAgent,
taskStore: new InMemoryTaskStore(),
port: 3000,
basePath: "/a2a",
card: {
name: "Example Agent",
url: "http://localhost:3000/a2a",
version: "1.0.0",
capabilities: { streaming: true },
skills: [{ id: "processor", name: "Text Processor" }],
},
});
myServer.start();
console.log("A2A Server running on http://localhost:3000/a2a");
Use FileStore
for file-based persistence. Ensure the directory exists.
import path from "path";
import fs from "fs";
const dataDir = path.join(process.cwd(), "a2a-data");
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
const myStore = new FileStore(dataDir);
const myServer = new A2AServer({
taskStore: myStore,
...
});
Use the built-in pino
-based logger. Configure with configureLogger
.
import { logger, configureLogger, LogLevel, logDebug } from "@artinet/sdk";
// Configure logging level (optional)
configureLogger({ level: "debug" });
logger.info("Server starting...");
//use helper functions
logDebug("LoggerTest", { taskId: "task-123" }, "Task status updated.");
// Create child logger with bound context
const taskLogger = logger.child({ taskId: "abc" });
taskLogger.info("Processing step X");
The SDK includes features to help make your agent discoverable:
- Automatic Registration: You can configure your
A2AServer
to automatically register yourAgentCard
with the A2A Registry upon startup by settingregister: true
(default:false
) in the server parameters.
const myServer = new A2AServer({
...
card: {
...
url: "http://my-public-domain:3000/my-agent", // Publicly accessible URL
...
},
register: true, // Enable automatic registration on start
});
- Custom Agent Card Path: By default, the server exposes its
AgentCard
at/.well-known/agent.json
RFC8615 and a fallback at/agent-card
. You can specify a different fallback path using thefallbackPath
option inA2AServerParams
.
const myServer = new A2AServer({
...
basePath: "/apiV2"
fallbackPath: "/apiV2/custom-card-info", // Agent card available here
...
});
// The AgentCard is now accessible at http://localhost:3001/apiV2/custom-card-info
Provide a custom createJSONRPCServer
function (implementing JSONRPCServerFactory
) for fine-grained control over the underlying RPC server.
This factory function receives objects of type CreateJSONRPCServerParams
& RequestParams
containing the necessary SDK dependencies (taskHandler
, taskStore
, agentCard
, activeCancellations
, createTaskContext
, closeStreamsForTask
) and the specific method parameters (i.e. SendMessageRequest["params"]
). You can use these dependencies to configure the standard A2A methods and add your own custom JSON-RPC methods.
The SDK exports default handlers for the standard A2A methods (e.g., defaultSendMessageMethod
), create your own using dedicated A2A method types(SendMessageMethod
) and use createJSONRPCMethod
to easily wrap these methods with dependency injection and error handling.
See src/server/lib/middleware/factory.ts
and src/server/lib/middleware/a2a-methods.ts
for implementation details.
Example:
const myCustomSendMethod: SendMessageMethod = (
deps,
requestParams,
callback
) => {
const { taskStore, taskHandler, createTaskContext } = deps;
const { id: taskId } = requestParams;
const { message, sessionId, metadata } = requestParams;
...
callback(null, ...);
};
const myCustomRPCServer: JSONRPCServerFactory = (
params: CreateJSONRPCServerParams
): JSONRPCServerType => {
//Use a custom message/send method
const messageSendMethod = createJSONRPCMethod(params, myCustomSendMethod, "message/send");
const taskGetMethod = createJSONRPCMethod(params, defaultGetTaskMethod, "tasks/get");
const taskCancelMethod = createJSONRPCMethod(params, defaultCancelTaskMethod, "tasks/cancel");
// Note: Push notifications are not fully implemented yet
const taskPushNotificationSetMethod = createJSONRPCMethod(params, defaultSetTaskPushNotificationMethod, "tasks/pushNotificationConfig/set");
const taskPushNotificationGetMethod = createJSONRPCMethod(params, defaultGetTaskPushNotificationMethod, "tasks/pushNotificationConfig/get");
const rpcServer = new JSONRPCServer({
"message/send": messageSendMethod,
"tasks/get": taskGetMethod,
"tasks/cancel": taskCancelMethod,
"tasks/pushNotificationConfig/set": taskPushNotificationSetMethod,
"tasks/pushNotificationConfig/get": taskPushNotificationGetMethod,
});
return rpcServer;
};
const server = new A2AServer({
createJSONRPCServer: myCustomRPCServer,
...
});
Using the Custom Factory
Pass your factory function via the createJSONRPCServer
option during A2AServer
initialization.
Important: The default A2AServer
setup automatically adds Express middleware to handle Server-Sent Events (SSE) for message/stream
and tasks/resubscribe
, as well as the /agent/card
(and /.well-known/agent.json
) GET endpoints. If you are not using A2AServer
and integrating the Jayson server middleware into your own Express application, you must implement these SSE and card endpoints yourself to maintain full A2A compliance, especially for streaming functionality. See src/server/lib/express-server.ts
for how the default server handles these routes.
We are excited to introduce new capabilities for deploying agents directly onto the artinet.
We've added a testDeployment
utility which is available for all users letting you bundle and test your agent logic in a temporary sandboxed environment.
QUICK-AGENTS Use the fullDeployment
utility, which allows direct deployment of your bundled agent code and AgentCard
to the Artinet platform (requires an ARTINET_API_KEY
).
To join the beta waitlist, please email us at [email protected] and stay tuned for more updates!
Key features include:
-
Easy Agent Bundling: Bundle your agent's code and dependencies into a single file using the
bundle
utility.import { bundle } from "@artinet/sdk"; const bundledCode = await bundle(new URL('./your-agent.ts', import.meta.url));
-
Sandboxed Enviroments: Streamline agent logic for quick and easy deployments. The new
artinet.v0
namespace (accessible via@artinet/sdk/agents
) providestaskManager
,connect
, andagent
.artinet.v0.taskManager
: Manages the agent's lifecycle by iterating over the agent'sTaskHandler
and communicating updates to the host environment.artinet.v0.connect
: Replaces the deprecatedfetchResponseProxy
. Allows agents to make proxied calls to other agents or LLMs via the host environment.artinet.v0.agent
: A factory function to obtain aClientProxy
for type-safe communication with other agents, managed by the host environment.
Example of using the new
artinet.v0
utilities in an agent:import { TaskContext, TaskYieldUpdate, Task } from "@artinet/sdk"; import { artinet } from "@artinet/sdk/agents"; export async function* myAgentLogic(context: TaskContext): AsyncGenerator<TaskYieldUpdate, Task | void, unknown> { yield { state: "working" }; // Call another agent/LLM using artinet.v0.connect const llmResponse = await artinet.v0.connect({ agentId: "SomeLLMAgentID", messages: [{ role: "user", content: "Tell me a joke." }] }); // Or communicate tasks with artinet.v0.agent const anotherAgent = artinet.v0.agent({ baseUrl: "https://agents.artinet.io/agentId=456", }); const taskResult = await anotherAgent.sendTask({ ... }); } // The host environment will invoke this taskManager with the agent's logic. await artinet.v0.taskManager({ taskHandler: myAgentLogic });
Note: The
taskHandlerProxy
andfetchResponseProxy
utilities are now deprecated in favor ofartinet.v0.taskManager
andartinet.v0.connect
respectively. -
Test-Agents (Experimental): Simulate and test your agents @ agents.artinet.io/test/deploy using the
testDeployment
tool.import { testDeployment, ServerDeploymentRequestParams, SendTaskRequest } from "@artinet/sdk"; const deploymentParams: ServerDeploymentRequestParams = { code: "/* bundled code string */", }; //create a list of tasks for your agent to complete once deployed const testRequests: SendTaskRequest[] = [ { id: "t1", message: { role: "user", parts: [{ type: "text", text: "Hi!" }] } } ]; for await (const result of testDeployment(deploymentParams, testRequests)) { console.log(result); //process the task completion requests as they come in to confirm your agents logic }
-
Full Deployment (Experimental): Deploy your agent to the Artinet platform using the
fullDeployment
utility.import { fullDeployment, ServerDeploymentRequestParams } from "@artinet/sdk"; const deploymentParams: ServerDeploymentRequestParams = { name: "My Awesome Agent", agentCard: { /* your agent card */ }, code: "/* bundled code string */", }; const deploymentResult = await fullDeployment(deploymentParams); // Requires an ARTINET_API_KEY environment variable console.log("Deployment Result:", deploymentResult);
-
Dedicated Endpoints: Once deployed your agent will be available On-Demand at its dedicated enpoint. (e.g. "https://agents.artinet.io/agentId=0xabf698845743538727a81352bfcfdb724e5c2bbe3113a26362482248f9f3e5fa/.well-known/agent.json")
-
New Types: To support these features, new types for server deployment requests and responses (such as
ServerDeploymentRequestParams
,ServerDeploymentResponse
) have been added tosrc/types/extended-schema.ts
. New types for sandboxed agent interactions (TaskProxy
,ConnectAPICallback
,ClientProxy
, etc.) are insrc/types/proxy.ts
.
QUICK-AGENT FAQs
- Test-Agents expire after 60s (need more time? let us know @[email protected])
- Quick-Agents do not have access to a filesystem or networking (limited persistance & networking capabalities are on the project roadmap).
- Quick-Agents v0 does not support streaming, push notifications or state transition history (these capabilities are on the project roadmap).
- Larger deployments can take significant time to deploy which may cause
fullDeployment
to timeout. In such cases wait to see if the listing has been added to your account before trying to deploy again. - Quick-Agent logic is public, therefore the artinet project is not liable for any sensitive material held within a deployment.
- Only availble with version 0.5.3 of the SDK.
Sign-up at artinet.io to deploy your Quick-Agent today!
Contributions are welcome! Please open an issue or submit a Pull Request on GitHub.
Ensure code adheres to the project style and passes linting (npm run lint
) and tests (npm test
).
This project is licensed under the Apache License 2.0 - see the LICENSE
file for details.
This SDK builds upon the concepts and specifications of the Agent2Agent (A2A) Protocol initiated by Google. It utilizes the official A2A JSON Schema for protocol compliance.
Libraries used include:
- Express.js for the server framework.
- Jayson for JSON-RPC handling.
- Pino for logging.
- EventSource Parser for SSE streaming.