Skip to main content
Glama

ado_upload_attachment

Upload files as attachments to Azure DevOps work items and retrieve attachment URLs to link documents directly to tasks, bugs, or features.

Instructions

Sube un archivo como adjunto a Azure DevOps y devuelve la URL del adjunto

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYesRuta completa del archivo a subir
fileNameNoNombre del archivo (opcional, se usa el nombre del archivo si no se especifica)

Implementation Reference

  • Handler for ado_upload_attachment tool.
    async ({ filePath, fileName }) => {
      if (!currentPat || !currentOrg) {
        throw new Error("No hay conexión configurada. Usa ado_configure primero.");
      }
    
      if (!fs.existsSync(filePath)) {
        throw new Error(`El archivo no existe: ${filePath}`);
      }
    
      const name = fileName || path.basename(filePath);
      const attachment = await uploadAttachmentRest(filePath, name);
    
      return {
        content: [
          {
            type: "text",
            text: `Archivo subido exitosamente:\n- Nombre: ${name}\n- URL: ${attachment.url}\n- ID: ${attachment.id}\n\nUsa esta URL con ado_add_attachment para vincular el adjunto a un Work Item.`,
          },
        ],
      };
    }
  • src/index.ts:781-809 (registration)
    Tool registration for ado_upload_attachment.
    server.tool(
      "ado_upload_attachment",
      "Sube un archivo como adjunto a Azure DevOps y devuelve la URL del adjunto",
      {
        filePath: z.string().describe("Ruta completa del archivo a subir"),
        fileName: z.string().optional().describe("Nombre del archivo (opcional, se usa el nombre del archivo si no se especifica)"),
      },
      async ({ filePath, fileName }) => {
        if (!currentPat || !currentOrg) {
          throw new Error("No hay conexión configurada. Usa ado_configure primero.");
        }
    
        if (!fs.existsSync(filePath)) {
          throw new Error(`El archivo no existe: ${filePath}`);
        }
    
        const name = fileName || path.basename(filePath);
        const attachment = await uploadAttachmentRest(filePath, name);
    
        return {
          content: [
            {
              type: "text",
              text: `Archivo subido exitosamente:\n- Nombre: ${name}\n- URL: ${attachment.url}\n- ID: ${attachment.id}\n\nUsa esta URL con ado_add_attachment para vincular el adjunto a un Work Item.`,
            },
          ],
        };
      }
    );
  • Helper function that performs the actual REST API call to upload the attachment.
    async function uploadAttachmentRest(
      filePath: string,
      fileName: string
    ): Promise<{ url: string; id: string }> {
      const fileContent = fs.readFileSync(filePath);
    
      // Construir URL del API - asegurar que no haya doble slash
      const baseUrl = currentOrg.endsWith("/") ? currentOrg.slice(0, -1) : currentOrg;
      const encodedProject = encodeURIComponent(currentProject);
      const encodedFileName = encodeURIComponent(fileName);
      const fullUrl = `${baseUrl}/${encodedProject}/_apis/wit/attachments?fileName=${encodedFileName}&api-version=7.0`;
    
      const urlObj = new URL(fullUrl);
    
      const options: https.RequestOptions = {
        hostname: urlObj.hostname,
        path: urlObj.pathname + urlObj.search,
        method: "POST",
        headers: {
          "Content-Type": "application/octet-stream",
          "Content-Length": fileContent.length,
          "Authorization": `Basic ${Buffer.from(`:${currentPat}`).toString("base64")}`,
        },
      };
    
      return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
          let data = "";
          res.on("data", (chunk) => (data += chunk));
          res.on("end", () => {
            if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
              try {
                const result = JSON.parse(data);
                resolve({ url: result.url, id: result.id });
              } catch (e) {
                reject(new Error(`Error parsing response: ${data}`));
              }
            } else {
              reject(new Error(`Error uploading attachment: ${res.statusCode} - ${data}`));
            }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/soulberto/mcp-azure'

If you have feedback or need assistance with the MCP directory API, please join our Discord server