@callsitehq/emit 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Callsite contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @callsitehq/emit
2
+
3
+ Pure renderers for Callsite artifacts.
4
+
5
+ Use this package to turn a Callsite IR into static agent-facing files.
6
+ The implemented renderers emit MCP tool configuration and OpenAPI 3.2.
7
+
8
+ ```ts
9
+ import { emitMcpJson, emitOpenApi } from "@callsitehq/emit";
10
+ ```
11
+
12
+ OpenAPI output is capability-shaped RPC over HTTP: each capability is rendered
13
+ as `POST /capabilities/{id}` with JSON input and output schemas.
14
+ Declared capability errors render as OpenAPI responses with a standard JSON
15
+ error envelope and as MCP Callsite metadata.
16
+
17
+ ## Status
18
+
19
+ Early `0.x` package. MCP and OpenAPI output are implemented first; other
20
+ surfaces are still being rebuilt against the current IR.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- import { CapabilityIR } from '@callsitehq/core';
1
+ import { IR, JsonObject } from '@callsitehq/core';
2
2
 
3
- interface EmitOptions {
3
+ interface EmitMcpJsonOptions {
4
4
  readonly name?: string;
5
5
  readonly version?: string;
6
+ }
7
+ interface EmitOpenApiOptions extends EmitMcpJsonOptions {
6
8
  readonly baseUrl?: string;
7
9
  }
8
- declare function emitMcpJson(ir: CapabilityIR, options?: EmitOptions): string;
9
- declare function emitOpenApi(ir: CapabilityIR, options?: EmitOptions): string;
10
- declare function emitChatGptAppConfig(ir: CapabilityIR, options?: EmitOptions): string;
11
- declare function emitClaudeConnectorConfig(ir: CapabilityIR, options?: EmitOptions): string;
10
+ type EmitOptions = EmitOpenApiOptions;
11
+ declare function emitMcpJson(ir: IR, options?: EmitMcpJsonOptions): string;
12
+ declare function mcpToolsFromIR(ir: IR): readonly JsonObject[];
13
+ declare function emitOpenApi(ir: IR, options?: EmitOpenApiOptions): string;
14
+ declare function emitChatGptAppConfig(ir: IR, options?: EmitMcpJsonOptions): string;
15
+ declare function emitClaudeConnectorConfig(ir: IR, options?: EmitMcpJsonOptions): string;
12
16
 
13
- export { type EmitOptions, emitChatGptAppConfig, emitClaudeConnectorConfig, emitMcpJson, emitOpenApi };
17
+ export { type EmitMcpJsonOptions, type EmitOpenApiOptions, type EmitOptions, emitChatGptAppConfig, emitClaudeConnectorConfig, emitMcpJson, emitOpenApi, mcpToolsFromIR };
package/dist/index.js CHANGED
@@ -1,89 +1,227 @@
1
1
  // src/index.ts
2
+ var CANONICAL_MCP_TOOL_FIELDS = /* @__PURE__ */ new Set(["name", "description", "inputSchema", "outputSchema"]);
3
+ var CANONICAL_OPENAPI_OPERATION_FIELDS = /* @__PURE__ */ new Set(["operationId", "requestBody", "responses"]);
4
+ var CALLSITE_ERRORS_META_KEY = "callsitehq.com/errors";
5
+ var STATUS_BY_ERROR_CODE = {
6
+ unauthorized: "401",
7
+ forbidden: "403",
8
+ not_found: "404",
9
+ conflict: "409",
10
+ rate_limited: "429",
11
+ unavailable: "503"
12
+ };
2
13
  function emitMcpJson(ir, options = {}) {
3
14
  return stringify({
4
15
  name: options.name ?? "callsite",
5
16
  version: options.version ?? "0.0.0",
6
- tools: ir.capabilities.map((capability) => ({
7
- name: capability.id,
8
- description: capability.intent,
9
- inputSchema: capability.inputSchema,
10
- annotations: {
11
- destructiveHint: capability.destructive
12
- }
13
- }))
17
+ tools: mcpToolsFromIR(ir)
14
18
  });
15
19
  }
20
+ function mcpToolsFromIR(ir) {
21
+ return ir.capabilities.map(capabilityToMcpTool);
22
+ }
16
23
  function emitOpenApi(ir, options = {}) {
17
- const paths = Object.fromEntries(
18
- ir.capabilities.map((capability) => [
19
- `/capabilities/${capability.id}`,
20
- capabilityToOpenApiPath(capability)
21
- ])
22
- );
23
24
  return stringify({
24
- openapi: "3.1.0",
25
+ openapi: "3.2.0",
25
26
  info: {
26
27
  title: options.name ?? "Callsite API",
27
28
  version: options.version ?? "0.0.0"
28
29
  },
29
30
  ...options.baseUrl === void 0 ? {} : { servers: [{ url: options.baseUrl }] },
30
- paths
31
+ paths: Object.fromEntries(
32
+ ir.capabilities.map((capability) => [
33
+ `/capabilities/${capability.id}`,
34
+ { post: capabilityToOpenApiOperation(capability) }
35
+ ])
36
+ )
31
37
  });
32
38
  }
33
39
  function emitChatGptAppConfig(ir, options = {}) {
34
- return stringify({
35
- name: options.name ?? "callsite",
36
- version: options.version ?? "0.0.0",
37
- tools: ir.capabilities.map((capability) => ({
38
- id: capability.id,
39
- description: capability.intent,
40
- input_schema: capability.inputSchema
41
- }))
42
- });
40
+ void ir;
41
+ void options;
42
+ throw new Error("emitChatGptAppConfig is not implemented for the current IR yet.");
43
43
  }
44
44
  function emitClaudeConnectorConfig(ir, options = {}) {
45
- return stringify({
46
- name: options.name ?? "callsite",
47
- version: options.version ?? "0.0.0",
48
- capabilities: ir.capabilities.map((capability) => ({
49
- name: capability.id,
50
- description: capability.intent,
51
- input_schema: capability.inputSchema
52
- }))
53
- });
45
+ void ir;
46
+ void options;
47
+ throw new Error("emitClaudeConnectorConfig is not implemented for the current IR yet.");
54
48
  }
55
- function capabilityToOpenApiPath(capability) {
56
- return {
57
- post: {
58
- operationId: capability.id.replaceAll(".", "_"),
59
- summary: capability.intent,
60
- requestBody: {
61
- required: true,
49
+ function capabilityToOpenApiOperation(capability) {
50
+ const override = capability.overrides.openapi ?? {};
51
+ const passthrough = capability.passthrough.openapi ?? {};
52
+ const operation = {
53
+ operationId: capability.id.replaceAll(".", "_"),
54
+ summary: capability.id,
55
+ description: capability.intent,
56
+ requestBody: {
57
+ required: true,
58
+ content: {
59
+ "application/json": {
60
+ schema: capability.input
61
+ }
62
+ }
63
+ },
64
+ responses: {
65
+ "200": {
66
+ description: "Capability result",
62
67
  content: {
63
68
  "application/json": {
64
- schema: capability.inputSchema
69
+ schema: capability.output
65
70
  }
66
71
  }
67
72
  },
68
- responses: {
69
- "200": {
70
- description: "Capability result",
71
- content: {
72
- "application/json": {
73
- schema: capability.outputSchema
73
+ ...openApiResponsesForCapability(capability)
74
+ },
75
+ "x-callsite-destructive": capability.destructive,
76
+ ...omitCanonicalOpenApiOperationFields(override)
77
+ };
78
+ return { ...operation, ...passthrough };
79
+ }
80
+ function capabilityToMcpTool(capability) {
81
+ assertObjectSchema(capability, "input", capability.input);
82
+ assertObjectSchema(capability, "output", capability.output);
83
+ const canonicalAnnotations = {
84
+ destructiveHint: capability.destructive
85
+ };
86
+ const override = capability.overrides.mcp ?? {};
87
+ const passthrough = capability.passthrough.mcp ?? {};
88
+ const overrideAnnotations = jsonObjectValue(override.annotations);
89
+ const meta = mcpMetaForCapability(capability, override);
90
+ const tool = {
91
+ name: capability.id,
92
+ description: capability.intent,
93
+ inputSchema: capability.input,
94
+ outputSchema: capability.output,
95
+ ...omitReservedMcpFields(override),
96
+ ...Object.keys(meta).length === 0 ? {} : { _meta: meta },
97
+ annotations: {
98
+ ...canonicalAnnotations,
99
+ ...overrideAnnotations,
100
+ destructiveHint: capability.destructive
101
+ }
102
+ };
103
+ return { ...tool, ...passthrough };
104
+ }
105
+ function assertObjectSchema(capability, direction, schema) {
106
+ if (schema.type !== "object") {
107
+ throw new TypeError(
108
+ `MCP ${direction} schema for capability "${capability.id}" must be a JSON object schema.`
109
+ );
110
+ }
111
+ }
112
+ function openApiResponsesForCapability(capability) {
113
+ return Object.fromEntries(
114
+ openApiErrorResponseDefinitions(capability).map((response) => [
115
+ response.status,
116
+ openApiErrorResponse(response)
117
+ ])
118
+ );
119
+ }
120
+ function openApiErrorResponseDefinitions(capability) {
121
+ const declaredErrorResponses = groupDeclaredErrorsByStatus(capability.errors).map(
122
+ declaredErrorGroupToOpenApiResponse
123
+ );
124
+ return [
125
+ {
126
+ status: "400",
127
+ description: "Invalid request",
128
+ codes: ["invalid_input"]
129
+ },
130
+ ...declaredErrorResponses,
131
+ {
132
+ status: "500",
133
+ description: "Capability error",
134
+ codes: ["internal"]
135
+ }
136
+ ];
137
+ }
138
+ function groupDeclaredErrorsByStatus(errors) {
139
+ const errorsByStatus = /* @__PURE__ */ new Map();
140
+ for (const error of errors) {
141
+ const status = STATUS_BY_ERROR_CODE[error.code];
142
+ const statusErrors = errorsByStatus.get(status) ?? [];
143
+ errorsByStatus.set(status, [...statusErrors, error]);
144
+ }
145
+ return Array.from(errorsByStatus, ([status, statusErrors]) => ({
146
+ status,
147
+ errors: statusErrors
148
+ }));
149
+ }
150
+ function declaredErrorGroupToOpenApiResponse(group) {
151
+ return {
152
+ status: group.status,
153
+ description: group.errors.map((error) => error.intent).join("\n"),
154
+ codes: unique(group.errors.map((error) => error.code))
155
+ };
156
+ }
157
+ function mcpMetaForCapability(capability, override) {
158
+ const overrideMeta = jsonObjectValue(override._meta);
159
+ return {
160
+ ...overrideMeta,
161
+ ...capability.errors.length === 0 ? {} : { [CALLSITE_ERRORS_META_KEY]: errorsToJson(capability.errors) }
162
+ };
163
+ }
164
+ function openApiErrorResponse(response) {
165
+ return {
166
+ description: response.description,
167
+ content: {
168
+ "application/json": {
169
+ schema: {
170
+ type: "object",
171
+ properties: {
172
+ error: {
173
+ type: "object",
174
+ properties: {
175
+ code: {
176
+ type: "string",
177
+ enum: response.codes
178
+ },
179
+ message: {
180
+ type: "string"
181
+ },
182
+ details: {
183
+ type: "object",
184
+ additionalProperties: true
185
+ }
186
+ },
187
+ required: ["code", "message"],
188
+ additionalProperties: false
74
189
  }
75
- }
76
- },
77
- "400": {
78
- description: "Invalid request"
79
- },
80
- "500": {
81
- description: "Capability error"
190
+ },
191
+ required: ["error"],
192
+ additionalProperties: false
82
193
  }
83
194
  }
84
195
  }
85
196
  };
86
197
  }
198
+ function errorsToJson(errors) {
199
+ return errors.map((error) => ({
200
+ code: error.code,
201
+ intent: error.intent
202
+ }));
203
+ }
204
+ function unique(values) {
205
+ return Array.from(new Set(values));
206
+ }
207
+ function omitReservedMcpFields(value) {
208
+ return Object.fromEntries(
209
+ Object.entries(value).filter(
210
+ ([key]) => key !== "annotations" && key !== "_meta" && !CANONICAL_MCP_TOOL_FIELDS.has(key)
211
+ )
212
+ );
213
+ }
214
+ function omitCanonicalOpenApiOperationFields(value) {
215
+ return Object.fromEntries(
216
+ Object.entries(value).filter(([key]) => !CANONICAL_OPENAPI_OPERATION_FIELDS.has(key))
217
+ );
218
+ }
219
+ function jsonObjectValue(value) {
220
+ if (value === void 0 || value === null || Array.isArray(value) || typeof value !== "object") {
221
+ return {};
222
+ }
223
+ return value;
224
+ }
87
225
  function stringify(value) {
88
226
  return `${JSON.stringify(value, null, 2)}
89
227
  `;
@@ -92,6 +230,7 @@ export {
92
230
  emitChatGptAppConfig,
93
231
  emitClaudeConnectorConfig,
94
232
  emitMcpJson,
95
- emitOpenApi
233
+ emitOpenApi,
234
+ mcpToolsFromIR
96
235
  };
97
236
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { CapabilityIR, CapabilityIRNode, JsonObject } from \"@callsitehq/core\";\n\nexport interface EmitOptions {\n readonly name?: string;\n readonly version?: string;\n readonly baseUrl?: string;\n}\n\nexport function emitMcpJson(ir: CapabilityIR, options: EmitOptions = {}): string {\n return stringify({\n name: options.name ?? \"callsite\",\n version: options.version ?? \"0.0.0\",\n tools: ir.capabilities.map((capability) => ({\n name: capability.id,\n description: capability.intent,\n inputSchema: capability.inputSchema,\n annotations: {\n destructiveHint: capability.destructive\n }\n }))\n });\n}\n\nexport function emitOpenApi(ir: CapabilityIR, options: EmitOptions = {}): string {\n const paths = Object.fromEntries(\n ir.capabilities.map((capability) => [\n `/capabilities/${capability.id}`,\n capabilityToOpenApiPath(capability)\n ])\n );\n\n return stringify({\n openapi: \"3.1.0\",\n info: {\n title: options.name ?? \"Callsite API\",\n version: options.version ?? \"0.0.0\"\n },\n ...(options.baseUrl === undefined ? {} : { servers: [{ url: options.baseUrl }] }),\n paths\n });\n}\n\nexport function emitChatGptAppConfig(ir: CapabilityIR, options: EmitOptions = {}): string {\n return stringify({\n name: options.name ?? \"callsite\",\n version: options.version ?? \"0.0.0\",\n tools: ir.capabilities.map((capability) => ({\n id: capability.id,\n description: capability.intent,\n input_schema: capability.inputSchema\n }))\n });\n}\n\nexport function emitClaudeConnectorConfig(ir: CapabilityIR, options: EmitOptions = {}): string {\n return stringify({\n name: options.name ?? \"callsite\",\n version: options.version ?? \"0.0.0\",\n capabilities: ir.capabilities.map((capability) => ({\n name: capability.id,\n description: capability.intent,\n input_schema: capability.inputSchema\n }))\n });\n}\n\nfunction capabilityToOpenApiPath(capability: CapabilityIRNode): JsonObject {\n return {\n post: {\n operationId: capability.id.replaceAll(\".\", \"_\"),\n summary: capability.intent,\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: capability.inputSchema\n }\n }\n },\n responses: {\n \"200\": {\n description: \"Capability result\",\n content: {\n \"application/json\": {\n schema: capability.outputSchema\n }\n }\n },\n \"400\": {\n description: \"Invalid request\"\n },\n \"500\": {\n description: \"Capability error\"\n }\n }\n }\n };\n}\n\nfunction stringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n"],"mappings":";AAQO,SAAS,YAAY,IAAkB,UAAuB,CAAC,GAAW;AAC/E,SAAO,UAAU;AAAA,IACf,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW;AAAA,IAC5B,OAAO,GAAG,aAAa,IAAI,CAAC,gBAAgB;AAAA,MAC1C,MAAM,WAAW;AAAA,MACjB,aAAa,WAAW;AAAA,MACxB,aAAa,WAAW;AAAA,MACxB,aAAa;AAAA,QACX,iBAAiB,WAAW;AAAA,MAC9B;AAAA,IACF,EAAE;AAAA,EACJ,CAAC;AACH;AAEO,SAAS,YAAY,IAAkB,UAAuB,CAAC,GAAW;AAC/E,QAAM,QAAQ,OAAO;AAAA,IACnB,GAAG,aAAa,IAAI,CAAC,eAAe;AAAA,MAClC,iBAAiB,WAAW,EAAE;AAAA,MAC9B,wBAAwB,UAAU;AAAA,IACpC,CAAC;AAAA,EACH;AAEA,SAAO,UAAU;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,OAAO,QAAQ,QAAQ;AAAA,MACvB,SAAS,QAAQ,WAAW;AAAA,IAC9B;AAAA,IACA,GAAI,QAAQ,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,KAAK,QAAQ,QAAQ,CAAC,EAAE;AAAA,IAC/E;AAAA,EACF,CAAC;AACH;AAEO,SAAS,qBAAqB,IAAkB,UAAuB,CAAC,GAAW;AACxF,SAAO,UAAU;AAAA,IACf,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW;AAAA,IAC5B,OAAO,GAAG,aAAa,IAAI,CAAC,gBAAgB;AAAA,MAC1C,IAAI,WAAW;AAAA,MACf,aAAa,WAAW;AAAA,MACxB,cAAc,WAAW;AAAA,IAC3B,EAAE;AAAA,EACJ,CAAC;AACH;AAEO,SAAS,0BAA0B,IAAkB,UAAuB,CAAC,GAAW;AAC7F,SAAO,UAAU;AAAA,IACf,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW;AAAA,IAC5B,cAAc,GAAG,aAAa,IAAI,CAAC,gBAAgB;AAAA,MACjD,MAAM,WAAW;AAAA,MACjB,aAAa,WAAW;AAAA,MACxB,cAAc,WAAW;AAAA,IAC3B,EAAE;AAAA,EACJ,CAAC;AACH;AAEA,SAAS,wBAAwB,YAA0C;AACzE,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,aAAa,WAAW,GAAG,WAAW,KAAK,GAAG;AAAA,MAC9C,SAAS,WAAW;AAAA,MACpB,aAAa;AAAA,QACX,UAAU;AAAA,QACV,SAAS;AAAA,UACP,oBAAoB;AAAA,YAClB,QAAQ,WAAW;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT,OAAO;AAAA,UACL,aAAa;AAAA,UACb,SAAS;AAAA,YACP,oBAAoB;AAAA,cAClB,QAAQ,WAAW;AAAA,YACrB;AAAA,UACF;AAAA,QACF;AAAA,QACA,OAAO;AAAA,UACL,aAAa;AAAA,QACf;AAAA,QACA,OAAO;AAAA,UACL,aAAa;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAAwB;AACzC,SAAO,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA;AAC1C;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type {\n CapabilityErrorCode,\n CapabilityErrorSpec,\n CapabilityIR,\n DeclaredCapabilityErrorCode,\n IR,\n JsonObject,\n JsonValue\n} from \"@callsitehq/core\";\n\nexport interface EmitMcpJsonOptions {\n readonly name?: string;\n readonly version?: string;\n}\n\nexport interface EmitOpenApiOptions extends EmitMcpJsonOptions {\n readonly baseUrl?: string;\n}\n\nexport type EmitOptions = EmitOpenApiOptions;\n\nconst CANONICAL_MCP_TOOL_FIELDS = new Set([\"name\", \"description\", \"inputSchema\", \"outputSchema\"]);\nconst CANONICAL_OPENAPI_OPERATION_FIELDS = new Set([\"operationId\", \"requestBody\", \"responses\"]);\nconst CALLSITE_ERRORS_META_KEY = \"callsitehq.com/errors\";\nconst STATUS_BY_ERROR_CODE: Record<DeclaredCapabilityErrorCode, string> = {\n unauthorized: \"401\",\n forbidden: \"403\",\n not_found: \"404\",\n conflict: \"409\",\n rate_limited: \"429\",\n unavailable: \"503\"\n};\n\ninterface OpenApiErrorResponseDefinition {\n readonly status: string;\n readonly description: string;\n readonly codes: readonly CapabilityErrorCode[];\n}\n\ninterface DeclaredErrorStatusGroup {\n readonly status: string;\n readonly errors: readonly CapabilityErrorSpec[];\n}\n\nexport function emitMcpJson(ir: IR, options: EmitMcpJsonOptions = {}): string {\n return stringify({\n name: options.name ?? \"callsite\",\n version: options.version ?? \"0.0.0\",\n tools: mcpToolsFromIR(ir)\n });\n}\n\nexport function mcpToolsFromIR(ir: IR): readonly JsonObject[] {\n return ir.capabilities.map(capabilityToMcpTool);\n}\n\nexport function emitOpenApi(ir: IR, options: EmitOpenApiOptions = {}): string {\n return stringify({\n openapi: \"3.2.0\",\n info: {\n title: options.name ?? \"Callsite API\",\n version: options.version ?? \"0.0.0\"\n },\n ...(options.baseUrl === undefined ? {} : { servers: [{ url: options.baseUrl }] }),\n paths: Object.fromEntries(\n ir.capabilities.map((capability) => [\n `/capabilities/${capability.id}`,\n { post: capabilityToOpenApiOperation(capability) }\n ])\n )\n });\n}\n\nexport function emitChatGptAppConfig(ir: IR, options: EmitMcpJsonOptions = {}): string {\n void ir;\n void options;\n throw new Error(\"emitChatGptAppConfig is not implemented for the current IR yet.\");\n}\n\nexport function emitClaudeConnectorConfig(ir: IR, options: EmitMcpJsonOptions = {}): string {\n void ir;\n void options;\n throw new Error(\"emitClaudeConnectorConfig is not implemented for the current IR yet.\");\n}\n\nfunction capabilityToOpenApiOperation(capability: CapabilityIR): JsonObject {\n const override = capability.overrides.openapi ?? {};\n const passthrough = capability.passthrough.openapi ?? {};\n const operation: JsonObject = {\n operationId: capability.id.replaceAll(\".\", \"_\"),\n summary: capability.id,\n description: capability.intent,\n requestBody: {\n required: true,\n content: {\n \"application/json\": {\n schema: capability.input\n }\n }\n },\n responses: {\n \"200\": {\n description: \"Capability result\",\n content: {\n \"application/json\": {\n schema: capability.output\n }\n }\n },\n ...openApiResponsesForCapability(capability)\n },\n \"x-callsite-destructive\": capability.destructive,\n ...omitCanonicalOpenApiOperationFields(override)\n };\n\n return { ...operation, ...passthrough };\n}\n\nfunction capabilityToMcpTool(capability: CapabilityIR): JsonObject {\n assertObjectSchema(capability, \"input\", capability.input);\n assertObjectSchema(capability, \"output\", capability.output);\n\n const canonicalAnnotations: JsonObject = {\n destructiveHint: capability.destructive\n };\n const override = capability.overrides.mcp ?? {};\n const passthrough = capability.passthrough.mcp ?? {};\n const overrideAnnotations = jsonObjectValue(override.annotations);\n const meta = mcpMetaForCapability(capability, override);\n\n const tool: JsonObject = {\n name: capability.id,\n description: capability.intent,\n inputSchema: capability.input,\n outputSchema: capability.output,\n ...omitReservedMcpFields(override),\n ...(Object.keys(meta).length === 0 ? {} : { _meta: meta }),\n annotations: {\n ...canonicalAnnotations,\n ...overrideAnnotations,\n destructiveHint: capability.destructive\n }\n };\n\n return { ...tool, ...passthrough };\n}\n\nfunction assertObjectSchema(\n capability: CapabilityIR,\n direction: \"input\" | \"output\",\n schema: JsonObject\n): void {\n if (schema.type !== \"object\") {\n throw new TypeError(\n `MCP ${direction} schema for capability \"${capability.id}\" must be a JSON object schema.`\n );\n }\n}\n\nfunction openApiResponsesForCapability(capability: CapabilityIR): JsonObject {\n return Object.fromEntries(\n openApiErrorResponseDefinitions(capability).map((response) => [\n response.status,\n openApiErrorResponse(response)\n ])\n );\n}\n\nfunction openApiErrorResponseDefinitions(\n capability: CapabilityIR\n): readonly OpenApiErrorResponseDefinition[] {\n const declaredErrorResponses = groupDeclaredErrorsByStatus(capability.errors).map(\n declaredErrorGroupToOpenApiResponse\n );\n\n return [\n {\n status: \"400\",\n description: \"Invalid request\",\n codes: [\"invalid_input\"]\n },\n ...declaredErrorResponses,\n {\n status: \"500\",\n description: \"Capability error\",\n codes: [\"internal\"]\n }\n ];\n}\n\nfunction groupDeclaredErrorsByStatus(\n errors: readonly CapabilityErrorSpec[]\n): readonly DeclaredErrorStatusGroup[] {\n const errorsByStatus = new Map<string, CapabilityErrorSpec[]>();\n\n for (const error of errors) {\n const status = STATUS_BY_ERROR_CODE[error.code];\n const statusErrors = errorsByStatus.get(status) ?? [];\n\n errorsByStatus.set(status, [...statusErrors, error]);\n }\n\n return Array.from(errorsByStatus, ([status, statusErrors]) => ({\n status,\n errors: statusErrors\n }));\n}\n\nfunction declaredErrorGroupToOpenApiResponse(\n group: DeclaredErrorStatusGroup\n): OpenApiErrorResponseDefinition {\n return {\n status: group.status,\n description: group.errors.map((error) => error.intent).join(\"\\n\"),\n codes: unique(group.errors.map((error) => error.code))\n };\n}\n\nfunction mcpMetaForCapability(capability: CapabilityIR, override: JsonObject): JsonObject {\n const overrideMeta = jsonObjectValue(override._meta);\n\n return {\n ...overrideMeta,\n ...(capability.errors.length === 0\n ? {}\n : { [CALLSITE_ERRORS_META_KEY]: errorsToJson(capability.errors) })\n };\n}\n\nfunction openApiErrorResponse(response: OpenApiErrorResponseDefinition): JsonObject {\n return {\n description: response.description,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n properties: {\n error: {\n type: \"object\",\n properties: {\n code: {\n type: \"string\",\n enum: response.codes\n },\n message: {\n type: \"string\"\n },\n details: {\n type: \"object\",\n additionalProperties: true\n }\n },\n required: [\"code\", \"message\"],\n additionalProperties: false\n }\n },\n required: [\"error\"],\n additionalProperties: false\n }\n }\n }\n };\n}\n\nfunction errorsToJson(errors: readonly CapabilityErrorSpec[]): JsonValue {\n return errors.map((error) => ({\n code: error.code,\n intent: error.intent\n }));\n}\n\nfunction unique<T>(values: readonly T[]): readonly T[] {\n return Array.from(new Set(values));\n}\n\nfunction omitReservedMcpFields(value: JsonObject): JsonObject {\n return Object.fromEntries(\n Object.entries(value).filter(\n ([key]) => key !== \"annotations\" && key !== \"_meta\" && !CANONICAL_MCP_TOOL_FIELDS.has(key)\n )\n );\n}\n\nfunction omitCanonicalOpenApiOperationFields(value: JsonObject): JsonObject {\n return Object.fromEntries(\n Object.entries(value).filter(([key]) => !CANONICAL_OPENAPI_OPERATION_FIELDS.has(key))\n );\n}\n\nfunction jsonObjectValue(value: JsonValue | undefined): JsonObject {\n if (value === undefined || value === null || Array.isArray(value) || typeof value !== \"object\") {\n return {};\n }\n\n return value as JsonObject;\n}\n\nfunction stringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n"],"mappings":";AAqBA,IAAM,4BAA4B,oBAAI,IAAI,CAAC,QAAQ,eAAe,eAAe,cAAc,CAAC;AAChG,IAAM,qCAAqC,oBAAI,IAAI,CAAC,eAAe,eAAe,WAAW,CAAC;AAC9F,IAAM,2BAA2B;AACjC,IAAM,uBAAoE;AAAA,EACxE,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AAAA,EACX,UAAU;AAAA,EACV,cAAc;AAAA,EACd,aAAa;AACf;AAaO,SAAS,YAAY,IAAQ,UAA8B,CAAC,GAAW;AAC5E,SAAO,UAAU;AAAA,IACf,MAAM,QAAQ,QAAQ;AAAA,IACtB,SAAS,QAAQ,WAAW;AAAA,IAC5B,OAAO,eAAe,EAAE;AAAA,EAC1B,CAAC;AACH;AAEO,SAAS,eAAe,IAA+B;AAC5D,SAAO,GAAG,aAAa,IAAI,mBAAmB;AAChD;AAEO,SAAS,YAAY,IAAQ,UAA8B,CAAC,GAAW;AAC5E,SAAO,UAAU;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,MACJ,OAAO,QAAQ,QAAQ;AAAA,MACvB,SAAS,QAAQ,WAAW;AAAA,IAC9B;AAAA,IACA,GAAI,QAAQ,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,KAAK,QAAQ,QAAQ,CAAC,EAAE;AAAA,IAC/E,OAAO,OAAO;AAAA,MACZ,GAAG,aAAa,IAAI,CAAC,eAAe;AAAA,QAClC,iBAAiB,WAAW,EAAE;AAAA,QAC9B,EAAE,MAAM,6BAA6B,UAAU,EAAE;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEO,SAAS,qBAAqB,IAAQ,UAA8B,CAAC,GAAW;AACrF,OAAK;AACL,OAAK;AACL,QAAM,IAAI,MAAM,iEAAiE;AACnF;AAEO,SAAS,0BAA0B,IAAQ,UAA8B,CAAC,GAAW;AAC1F,OAAK;AACL,OAAK;AACL,QAAM,IAAI,MAAM,sEAAsE;AACxF;AAEA,SAAS,6BAA6B,YAAsC;AAC1E,QAAM,WAAW,WAAW,UAAU,WAAW,CAAC;AAClD,QAAM,cAAc,WAAW,YAAY,WAAW,CAAC;AACvD,QAAM,YAAwB;AAAA,IAC5B,aAAa,WAAW,GAAG,WAAW,KAAK,GAAG;AAAA,IAC9C,SAAS,WAAW;AAAA,IACpB,aAAa,WAAW;AAAA,IACxB,aAAa;AAAA,MACX,UAAU;AAAA,MACV,SAAS;AAAA,QACP,oBAAoB;AAAA,UAClB,QAAQ,WAAW;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,IACA,WAAW;AAAA,MACT,OAAO;AAAA,QACL,aAAa;AAAA,QACb,SAAS;AAAA,UACP,oBAAoB;AAAA,YAClB,QAAQ,WAAW;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,MACA,GAAG,8BAA8B,UAAU;AAAA,IAC7C;AAAA,IACA,0BAA0B,WAAW;AAAA,IACrC,GAAG,oCAAoC,QAAQ;AAAA,EACjD;AAEA,SAAO,EAAE,GAAG,WAAW,GAAG,YAAY;AACxC;AAEA,SAAS,oBAAoB,YAAsC;AACjE,qBAAmB,YAAY,SAAS,WAAW,KAAK;AACxD,qBAAmB,YAAY,UAAU,WAAW,MAAM;AAE1D,QAAM,uBAAmC;AAAA,IACvC,iBAAiB,WAAW;AAAA,EAC9B;AACA,QAAM,WAAW,WAAW,UAAU,OAAO,CAAC;AAC9C,QAAM,cAAc,WAAW,YAAY,OAAO,CAAC;AACnD,QAAM,sBAAsB,gBAAgB,SAAS,WAAW;AAChE,QAAM,OAAO,qBAAqB,YAAY,QAAQ;AAEtD,QAAM,OAAmB;AAAA,IACvB,MAAM,WAAW;AAAA,IACjB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB,cAAc,WAAW;AAAA,IACzB,GAAG,sBAAsB,QAAQ;AAAA,IACjC,GAAI,OAAO,KAAK,IAAI,EAAE,WAAW,IAAI,CAAC,IAAI,EAAE,OAAO,KAAK;AAAA,IACxD,aAAa;AAAA,MACX,GAAG;AAAA,MACH,GAAG;AAAA,MACH,iBAAiB,WAAW;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO,EAAE,GAAG,MAAM,GAAG,YAAY;AACnC;AAEA,SAAS,mBACP,YACA,WACA,QACM;AACN,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,IAAI;AAAA,MACR,OAAO,SAAS,2BAA2B,WAAW,EAAE;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,SAAS,8BAA8B,YAAsC;AAC3E,SAAO,OAAO;AAAA,IACZ,gCAAgC,UAAU,EAAE,IAAI,CAAC,aAAa;AAAA,MAC5D,SAAS;AAAA,MACT,qBAAqB,QAAQ;AAAA,IAC/B,CAAC;AAAA,EACH;AACF;AAEA,SAAS,gCACP,YAC2C;AAC3C,QAAM,yBAAyB,4BAA4B,WAAW,MAAM,EAAE;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,MACE,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,OAAO,CAAC,eAAe;AAAA,IACzB;AAAA,IACA,GAAG;AAAA,IACH;AAAA,MACE,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,OAAO,CAAC,UAAU;AAAA,IACpB;AAAA,EACF;AACF;AAEA,SAAS,4BACP,QACqC;AACrC,QAAM,iBAAiB,oBAAI,IAAmC;AAE9D,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,qBAAqB,MAAM,IAAI;AAC9C,UAAM,eAAe,eAAe,IAAI,MAAM,KAAK,CAAC;AAEpD,mBAAe,IAAI,QAAQ,CAAC,GAAG,cAAc,KAAK,CAAC;AAAA,EACrD;AAEA,SAAO,MAAM,KAAK,gBAAgB,CAAC,CAAC,QAAQ,YAAY,OAAO;AAAA,IAC7D;AAAA,IACA,QAAQ;AAAA,EACV,EAAE;AACJ;AAEA,SAAS,oCACP,OACgC;AAChC,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM,OAAO,IAAI,CAAC,UAAU,MAAM,MAAM,EAAE,KAAK,IAAI;AAAA,IAChE,OAAO,OAAO,MAAM,OAAO,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AAAA,EACvD;AACF;AAEA,SAAS,qBAAqB,YAA0B,UAAkC;AACxF,QAAM,eAAe,gBAAgB,SAAS,KAAK;AAEnD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,WAAW,OAAO,WAAW,IAC7B,CAAC,IACD,EAAE,CAAC,wBAAwB,GAAG,aAAa,WAAW,MAAM,EAAE;AAAA,EACpE;AACF;AAEA,SAAS,qBAAqB,UAAsD;AAClF,SAAO;AAAA,IACL,aAAa,SAAS;AAAA,IACtB,SAAS;AAAA,MACP,oBAAoB;AAAA,QAClB,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,YAAY;AAAA,YACV,OAAO;AAAA,cACL,MAAM;AAAA,cACN,YAAY;AAAA,gBACV,MAAM;AAAA,kBACJ,MAAM;AAAA,kBACN,MAAM,SAAS;AAAA,gBACjB;AAAA,gBACA,SAAS;AAAA,kBACP,MAAM;AAAA,gBACR;AAAA,gBACA,SAAS;AAAA,kBACP,MAAM;AAAA,kBACN,sBAAsB;AAAA,gBACxB;AAAA,cACF;AAAA,cACA,UAAU,CAAC,QAAQ,SAAS;AAAA,cAC5B,sBAAsB;AAAA,YACxB;AAAA,UACF;AAAA,UACA,UAAU,CAAC,OAAO;AAAA,UAClB,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,aAAa,QAAmD;AACvE,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC5B,MAAM,MAAM;AAAA,IACZ,QAAQ,MAAM;AAAA,EAChB,EAAE;AACJ;AAEA,SAAS,OAAU,QAAoC;AACrD,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACnC;AAEA,SAAS,sBAAsB,OAA+B;AAC5D,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAK,EAAE;AAAA,MACpB,CAAC,CAAC,GAAG,MAAM,QAAQ,iBAAiB,QAAQ,WAAW,CAAC,0BAA0B,IAAI,GAAG;AAAA,IAC3F;AAAA,EACF;AACF;AAEA,SAAS,oCAAoC,OAA+B;AAC1E,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,mCAAmC,IAAI,GAAG,CAAC;AAAA,EACtF;AACF;AAEA,SAAS,gBAAgB,OAA0C;AACjE,MAAI,UAAU,UAAa,UAAU,QAAQ,MAAM,QAAQ,KAAK,KAAK,OAAO,UAAU,UAAU;AAC9F,WAAO,CAAC;AAAA,EACV;AAEA,SAAO;AACT;AAEA,SAAS,UAAU,OAAwB;AACzC,SAAO,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA;AAC1C;","names":[]}
package/package.json CHANGED
@@ -1,7 +1,24 @@
1
1
  {
2
2
  "name": "@callsitehq/emit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pure renderers from Callsite IR to agent-facing artifacts.",
5
+ "keywords": [
6
+ "agents",
7
+ "capabilities",
8
+ "mcp",
9
+ "openapi",
10
+ "typescript"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://github.com/callsitehq/callsite/tree/main/packages/emit#readme",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/callsitehq/callsite.git",
17
+ "directory": "packages/emit"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/callsitehq/callsite/issues"
21
+ },
5
22
  "type": "module",
6
23
  "exports": {
7
24
  ".": {
@@ -12,9 +29,12 @@
12
29
  "files": [
13
30
  "dist"
14
31
  ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
15
35
  "sideEffects": false,
16
36
  "dependencies": {
17
- "@callsitehq/core": "0.1.0"
37
+ "@callsitehq/core": "0.2.0"
18
38
  },
19
39
  "scripts": {
20
40
  "build": "tsup",