KJDRAW / CONNECT YOUR MODEL
Connect your model
Bring your model, gateway or agent framework. Keep one CAD engine and one set of drawing tools.
Choose a connection
KJDraw does not require a particular AI vendor. Choose a protocol adapter, supply your model and host transport, and run the same CAD tools. A custom KJAgentModel connects frameworks, local models or other protocols without changing the drawing engine.
Agent and client matrix
The drawing contract is independent of the model vendor. A client that can launch an stdio MCP server can use the same KJDraw tools; clients without native MCP support can connect through an MCP-capable host or call the TypeScript adapter directly.
| Client or model family | Recommended path | What changes | What stays the same |
|---|---|---|---|
| OpenAI Codex | Register the kjdraw stdio server in Codex MCP settings | Client configuration only | Tool names, proposals and approval boundary |
| Claude Desktop, Cursor, Cline | Add the same server entry to the client's MCP configuration | Client configuration only | KJD/DXF document contract |
| Kimi Code, WorkBuddy, ZCode, TraeCode | Run the user-level installer and follow the client's import prompt | Client configuration and one-time import | Local files and host approval |
| Doubao, DeepSeek and other domestic models | Use an MCP-capable host, or pass their tool-call JSON through createKJModelAdapter / createKJDomesticModelAdapter | Provider endpoint and transport | CAD tools, validation and receipts |
| Custom harness, gateway or private model | Implement KJAgentModel or expose the stdio server from the host | Your conversation loop and credentials | The KJDraw agent session and document model |
Portable MCP entry for a locally built package containing kjdraw-mcp.mjs: create proposals and results in the host project and replace both absolute-path placeholders. Verify the installed npm version first; next may still resolve to an older candidate without this executable.
{
"mcpServers": {
"kjdraw": {
"command": "node",
"args": ["/absolute/project/node_modules/@kanjieteam/kjdraw/bin/kjdraw-mcp.mjs", "--workspace", "/absolute/project", "--blank", "drawing.kjd", "--units", "millimeter", "--proposal-dir", "proposals", "--candidate-dir", "results"]
}
}
}
KJDraw does not discover provider keys, choose network endpoints or approve edits. Keep those responsibilities in the client or host and treat model text as untrusted input.
| Connection | Adapter value | Host transport |
|---|---|---|
| OpenAI Responses | responses | Responses REST endpoint |
| Chat Completions-compatible services | chat-completions | The selected service's compatible endpoint; DeepSeek is one evaluation option |
| Claude Messages | anthropic-messages | Messages REST endpoint |
| Gemini GenerateContent | gemini-generate-content | GenerateContent REST endpoint, with model in the URL |
| Your framework, gateway or local model | Custom KJAgentModel | Your own conversation bridge |
The built-in adapters support the protocols listed above. Compatibility with a particular model or vendor extension depends on that endpoint's function-calling behavior. For text-only models, a custom bridge can parse and validate a structured response; never execute model-generated JavaScript.
Wire the model once
Install KJDraw, then import the model adapter and bounded task runner from their public package entries:
import { createKJDrawSDK } from '@kanjieteam/kjdraw'
import { KJAgentToolSession } from '@kanjieteam/kjdraw/agent-tools'
import { createKJModelAdapter } from '@kanjieteam/kjdraw/model-adapters'
import { runKJAgentTask } from '@kanjieteam/kjdraw/agent-runner'
const sdk = createKJDrawSDK()
const drawing = sdk.createDocument({ units: 'millimeter' })
const session = new KJAgentToolSession(sdk, drawing)
const model = createKJModelAdapter({
protocol: 'chat-completions',
model: selectedModel,
request: ({ body, signal }) => hostModelGateway(body, signal),
})
const result = await runKJAgentTask({
session,
model,
prompt: 'Propose a circle at (20, 25) mm with a radius of 3 mm.',
toolNames: ['cad_read_drawing', 'cad_propose_circles'],
maxTurns: 8,
maxToolCalls: 32,
})
selectedModel and hostModelGateway belong to your application. The gateway returns parsed, non-streaming provider JSON and rejects HTTP failures. For Gemini, send the body to the configured model URL; this is a REST adapter, not the Google SDK's nested config argument. Keep keys, endpoint allowlists and user permissions on your server. The CAD package does not discover keys, choose an endpoint or send network requests by itself.
Chat-compatible endpoints differ in output token fields: the default is max_tokens; set chatTokenParameter: 'max_completion_tokens' when required. The other adapters map maxOutputTokens to their protocol. Full runtime argument validation remains enabled; Responses explicitly uses non-strict tool generation rather than promising identical provider-side schema support.
Doubao through Volcengine Ark
The source-tree adapter accepts provider doubao on Ark's Chat Completions protocol. Your trusted host supplies the model ID, HTTPS transport and ARK_API_KEY; KJDraw never reads that key. Ark's documented base URL is https://ark.cn-beijing.volces.com/api/v3 and the request path is /chat/completions. Enable tool calling on a model that supports it. The adapter preserves an assistant's encrypted reasoning block across tool-result turns, including a complete block delivered in a streaming response.
import { createKJDomesticModelAdapter } from '@kanjieteam/kjdraw/domestic-model-profiles'
const model = createKJDomesticModelAdapter({ provider: 'doubao', model: selectedArkModelId, reasoning: { mode: 'enabled' }, request: ({ body, signal }) => callTrustedArkGateway(body, signal), })
The model ID and gateway function above are provided by your host. The Ark Chat API and thinking/tool-call guide define the wire fields. Local protocol fixtures pass, but no live Ark account or specific model has been certified by those tests.
Choose tools for a task
toolNames is an optional host policy for one run. Omit it to retain all session tools. Supply a nonempty list of unique exact names from session.definitions; unknown names, duplicates and empty lists fail before opening a model conversation. Definitions retain their canonical order and complete schemas, including drawing units. The runner snapshots the selection before invoking the model, so later array changes cannot widen access.
Every adapter and custom bridge receives the same selected definitions. If a response requests an omitted tool, the runner returns failed with KJAGENT_TOOL_NOT_ALLOWED before dispatching any call in that batch. The next run selects its own policy. This does not restrict trusted host calls made directly on the session, replace authentication, or allow the model to approve proposals. Include reading and pagination tools when the task needs them. Smaller schemas reduce JSON bytes; actual model token counts and task success still require provider measurements.
Review the result
| Status | What the host should do |
|---|---|
awaiting-approval | Display the exact proposals in outputs; use proposalIds to approve or reject from your review UI |
responded | Show the model's answer or clarification; it is not evidence that a drawing task succeeded |
limit-reached | Inspect results and adjust the task or budget; do not automatically retry forever |
cancelled | The host cancelled or the run timed out; late model calls are not dispatched |
failed | Handle the structured error; inspect private transport diagnostics on the server |
The runner stops when it has proposals. It never calls approve(). After an authenticated user reviews the exact arguments, your host calls session.approve(planId, user.id) or session.reject(planId, user.id) and checks the result. Model text must be displayed as untrusted text, not unsanitized HTML.
Each run starts from the drawing's current revision. After applying a proposal, submit the next request as a new run so the model reads the updated geometry. Your application owns conversation persistence and save policy. See Agent workflows for CAD tools, proposal previews and geometry checks.
To expose the same tool registry through a packaged local stdio server, follow the MCP integration guide. It includes persistent client configuration, a real JSON-RPC call and the host-controlled file and approval boundary.
Measure tokens and time
result.measurements retains each attempted turn's usage, normalized totals, transport wall time and runner wall time. Counters come from provider response fields; missing or invalid counters remain null. Cache and reasoning counters are subsets or additional components according to each protocol, so they are not blindly added twice. A cancelled request without a received response has missing usage. complete describes observed token counters, not design completion or a billing receipt.
const { totals, transportWallMs, runWallMs, complete } = result.measurements
// Keep null as unavailable; do not replace it with zero in a benchmark.
console.log({ totals, transportWallMs, runWallMs, complete })
Adapters also accept onUsage: usage => hostMetrics.record(usage) for response observations, including rejected or truncated responses. Observer failures do not change drawing behavior. A late response after cancellation may still reach that host observer; it cannot rewrite the runner's returned measurement snapshot. extractKJModelUsage is available from /model-usage for a custom transport. No response text, credentials, inferred token counts or prices are included. Controlled live-model comparisons still require repeated matched tasks and actual provider configuration.
Run the packaged example
node node_modules/@kanjieteam/kjdraw/examples/model-agent.mjs
By default, the example runs offline and demonstrates all four wire formats, host approval, saved geometry and undo without contacting a model.
To connect an online model, configure KJDRAW_MODEL_PROTOCOL, KJDRAW_MODEL_NAME, KJDRAW_MODEL_ENDPOINT and KJDRAW_MODEL_API_KEY in a trusted server or CLI environment, then add --live. The endpoint is the complete trusted REST URL. The example rejects redirects and URL credentials, limits response bytes and never applies live proposals automatically. It sends at most four model requests and eight tool calls; provider charges may apply.
Extend and validate
Implement KJAgentModel.createConversation({ instructions, tools }) and return next(input, signal). A turn returns text plus calls containing id, name and arguments. Inputs are either the initial prompt or ordered tool results. Keep vendor continuation data private to that conversation and preserve call/result IDs. Existing Agent runtimes can also use KJAgentToolSession directly and manage their own execution loop.
Adapters retain Responses reasoning items, chat reasoning fields, Claude signed thinking blocks and Gemini thought signatures in their original conversation. They do not place those fields in the public answer. Truncated, blocked, malformed or unsupported responses stop before tool dispatch. Tool validation errors can be returned to the model for bounded correction. Timeouts cannot stop a transport that ignores its signal from consuming remote resources; the host must enforce its own network and billing limits.
Protocol references: OpenAI function calling, DeepSeek tool calls, Claude tool results, Gemini GenerateContent.