@pyai/mcp 0.4.0 → 0.5.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/AGENT_GUIDE.md +12 -0
- package/README.md +12 -0
- package/dist/extra-tools.js +1 -0
- package/dist/server.js +1 -1
- package/dist/tools.js +31 -4
- package/package.json +1 -1
package/AGENT_GUIDE.md
CHANGED
|
@@ -72,3 +72,15 @@ the corresponding session. Read it with `omni_get_call`, `omni_get_summary`,
|
|
|
72
72
|
`omni_get_transcript`, or `omni_get_recording` with `response: "url"`. Recording
|
|
73
73
|
URLs require REST authentication and are not public download links. For inbound
|
|
74
74
|
calls use `omni_list_calls`, filtering by `session_label`.
|
|
75
|
+
|
|
76
|
+
## Discovery and identity output
|
|
77
|
+
|
|
78
|
+
In MCP 0.5.0, use `discover_tools({"query":"voice agent"})`; `task` is an
|
|
79
|
+
alias. If both are present, they must match after trimming. Omit both to list
|
|
80
|
+
workflows. Empty or conflicting values return `invalid_arguments`.
|
|
81
|
+
|
|
82
|
+
`whoami({})` returns concise text showing environment, scopes and account
|
|
83
|
+
limits. Authorized organization, project and key IDs remain in structured
|
|
84
|
+
output; `whoami({"verbose":true})` also includes them in text. This reduces
|
|
85
|
+
prompt clutter, not host access to identity metadata. Credential-shaped fields
|
|
86
|
+
are omitted in both modes. Never paste keys or full identity dumps into reports.
|
package/README.md
CHANGED
|
@@ -126,3 +126,15 @@ Calling reaches real phone destinations and is not a sandbox simulation.
|
|
|
126
126
|
Use the SDK's `pyai.omni.connect({ rate, configure })` for realtime audio. The
|
|
127
127
|
[Omni frame contract](https://api.pyai.com/omni-frames.json) is generated from
|
|
128
128
|
the existing AsyncAPI protocol source.
|
|
129
|
+
|
|
130
|
+
## Discovery and identity output
|
|
131
|
+
|
|
132
|
+
In MCP 0.5.0, use `discover_tools({"query":"voice agent"})`; `task` is an
|
|
133
|
+
alias. If both are present, they must match after trimming. Omit both to list
|
|
134
|
+
workflows. Empty or conflicting values return `invalid_arguments`.
|
|
135
|
+
|
|
136
|
+
`whoami({})` returns concise text showing environment, scopes and account
|
|
137
|
+
limits. Authorized organization, project and key IDs remain in structured
|
|
138
|
+
output; `whoami({"verbose":true})` also includes them in text. This reduces
|
|
139
|
+
prompt clutter, not host access to identity metadata. Credential-shaped fields
|
|
140
|
+
are omitted in both modes. Never paste keys or full identity dumps into reports.
|
package/dist/extra-tools.js
CHANGED
|
@@ -18,6 +18,7 @@ export const EXTRA_TOOLS = [
|
|
|
18
18
|
description: "Find the right PyAI tools and a short recipe for a use case: speech, STT, voice agents, compliance, summaries, dubbing or narration. No credentials or usage.",
|
|
19
19
|
inputSchema: obj({
|
|
20
20
|
query: string("Product name or task, e.g. “translate audio”, “voice agent”, “TTS”."),
|
|
21
|
+
task: string("Alias for query. If both are supplied, they must match after trimming."),
|
|
21
22
|
}),
|
|
22
23
|
},
|
|
23
24
|
{
|
package/dist/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
2
2
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, McpError, ErrorCode, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { TOOLS, TOOL_SCOPES, callTool, localContext, } from "./tools.js";
|
|
4
4
|
import { GUIDE, RESOURCES, SERVER_INSTRUCTIONS, WORKFLOWS, } from "./workflows.js";
|
|
5
|
-
export const MCP_VERSION = "0.
|
|
5
|
+
export const MCP_VERSION = "0.5.0";
|
|
6
6
|
export function createMcpServer(client, context = localContext) {
|
|
7
7
|
const server = new Server({
|
|
8
8
|
name: "pyai",
|
package/dist/tools.js
CHANGED
|
@@ -45,6 +45,28 @@ function text(value) {
|
|
|
45
45
|
: { text: s },
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
+
// Keep authorized resource IDs available to integrations without filling the
|
|
49
|
+
// default conversational text with identity metadata. Never return credential fields.
|
|
50
|
+
function identityResult(value, verbose) {
|
|
51
|
+
function safe(item) {
|
|
52
|
+
if (Array.isArray(item))
|
|
53
|
+
return item.map(safe);
|
|
54
|
+
if (!item || typeof item !== "object")
|
|
55
|
+
return item;
|
|
56
|
+
return Object.fromEntries(Object.entries(item).filter(([key, entry]) => !/secret|password|authorization|access_?token|refresh_?token|api_?key|token_ref/i.test(key)
|
|
57
|
+
&& !(typeof entry === "string" && /^(key|token)$/i.test(key))).map(([key, entry]) => [key, safe(entry)]));
|
|
58
|
+
}
|
|
59
|
+
const identity = safe(value);
|
|
60
|
+
const summary = Object.fromEntries([
|
|
61
|
+
"env", "status", "org_status", "scopes", "expires_at", "plan", "limits", "credit", "key_budget_cents",
|
|
62
|
+
].filter(key => Object.hasOwn(identity, key)).map(key => [key, identity[key]]));
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: JSON.stringify(verbose ? identity : {
|
|
65
|
+
...summary, details: "Authorized resource IDs are in structuredContent. Use verbose=true to include identity details in text.",
|
|
66
|
+
}, null, 2) }],
|
|
67
|
+
structuredContent: identity,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
48
70
|
/** Tools that need a configured key. create_sandbox_key + get_started do not. */
|
|
49
71
|
const KEY_REQUIRED = new Set([
|
|
50
72
|
"list_models",
|
|
@@ -100,10 +122,10 @@ const LEGACY_TOOLS = [
|
|
|
100
122
|
{
|
|
101
123
|
name: "whoami",
|
|
102
124
|
description: "Introspect the active key (GET /v1/me): org, environment (test/live), granted scopes, " +
|
|
103
|
-
"and rate-limit/credit posture. Use it to self-diagnose a 401/403/402.",
|
|
125
|
+
"and rate-limit/credit posture. Text is concise; authorized IDs remain in structuredContent. Use verbose=true for identity details. Use it to self-diagnose a 401/403/402.",
|
|
104
126
|
inputSchema: {
|
|
105
127
|
type: "object",
|
|
106
|
-
properties: {},
|
|
128
|
+
properties: { verbose: { type: "boolean", description: "Include authorized identity details in text. Credentials are always omitted.", default: false } },
|
|
107
129
|
additionalProperties: false,
|
|
108
130
|
},
|
|
109
131
|
},
|
|
@@ -464,7 +486,12 @@ async function executeTool(name, args, client, ctx) {
|
|
|
464
486
|
},
|
|
465
487
|
};
|
|
466
488
|
if (name === "discover_tools") {
|
|
467
|
-
const
|
|
489
|
+
const query = typeof args.query === "string" ? args.query.trim() : undefined;
|
|
490
|
+
const task = typeof args.task === "string" ? args.task.trim() : undefined;
|
|
491
|
+
if (query === "" || task === "" || (query !== undefined && task !== undefined && query !== task)) {
|
|
492
|
+
return toolError("Use a non-empty query or task. If both are supplied, they must match after trimming.", "invalid_arguments");
|
|
493
|
+
}
|
|
494
|
+
const words = String(query ?? task ?? "")
|
|
468
495
|
.toLowerCase()
|
|
469
496
|
.match(/[a-z0-9]+/g)
|
|
470
497
|
?.filter((w) => ![
|
|
@@ -549,7 +576,7 @@ async function executeTool(name, args, client, ctx) {
|
|
|
549
576
|
});
|
|
550
577
|
}
|
|
551
578
|
case "whoami":
|
|
552
|
-
return
|
|
579
|
+
return identityResult(await client.getJson("/v1/me"), args.verbose === true);
|
|
553
580
|
case "list_models":
|
|
554
581
|
return text(await client.getJson("/v1/models"));
|
|
555
582
|
case "list_voices": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "PyAI MCP: OAuth-connected voice agents, speech, transcription, compliance, call summaries, dubbing and expressive narration for chat apps and coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|